diff --git a/db/schema_phase12_qibing_advanced.sql b/db/schema_phase12_qibing_advanced.sql
new file mode 100644
index 0000000..47e1ea1
--- /dev/null
+++ b/db/schema_phase12_qibing_advanced.sql
@@ -0,0 +1,85 @@
+-- ============================================================================
+-- 优匠优服 · Phase 12 对标奇兵到家业务体系高级演进
+-- 1. 多商品组合明细表 fa_order_goods
+-- 2. 主订单表 fa_order 扩展超时转单、空跑费与施工增项补差价
+-- ============================================================================
+SET NAMES utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `fa_order_goods` (
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `order_id` INT(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '所属主订单ID',
+ `service_id` INT(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '服务类目ID',
+ `goods_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '商品名称',
+ `goods_model` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '规格型号',
+ `goods_num` INT(11) NOT NULL DEFAULT 1 COMMENT '商品数量',
+ `goods_image` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '商品实物照片',
+ `addon_services` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '增项服务(拆旧/打孔/切割)',
+ `base_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '单项服务基础价格',
+ `createtime` INT(11) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ INDEX `idx_order` (`order_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单多商品组合明细表';
+
+-- 检查并添加字段
+SET @dbname = DATABASE();
+SET @tablename = "fa_order";
+
+-- auto_quote_timeout
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'auto_quote_timeout') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `auto_quote_timeout` INT(11) NOT NULL DEFAULT 0 COMMENT "超时转报价单时长(秒)"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
+
+-- empty_run_fee
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'empty_run_fee') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `empty_run_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT "空跑补偿费"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
+
+-- diff_amount
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'diff_amount') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `diff_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT "增项补差价金额"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
+
+-- diff_status
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'diff_status') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `diff_status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "补差价状态:0无,1待支付,2已支付"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
+
+-- diff_remark
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'diff_remark') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `diff_remark` VARCHAR(255) NOT NULL DEFAULT "" COMMENT "补差价原因"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
+
+-- diff_image
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = 'diff_image') > 0,
+ 'SELECT 1',
+ 'ALTER TABLE `fa_order` ADD COLUMN `diff_image` VARCHAR(255) NOT NULL DEFAULT "" COMMENT "补差价凭据图"'
+));
+PREPARE alterIfNotExists FROM @preparedStatement;
+EXECUTE alterIfNotExists;
+DEALLOCATE PREPARE alterIfNotExists;
diff --git a/server/addons/cos/.addonrc b/server/addons/cos/.addonrc
new file mode 100644
index 0000000..a0526cf
--- /dev/null
+++ b/server/addons/cos/.addonrc
@@ -0,0 +1 @@
+{"files":["public\\assets\\addons\\cos\\js\\spark.js"],"license":"extended","licenseto":"26425","licensekey":"OaAxjC9Us61P0HZy SoRT2iPVtXFTb8ql3MIR6A==","domains":["yjyf"],"licensecodes":[],"validations":["4dceabe25bd363f318cc03da6a62d391"]}
\ No newline at end of file
diff --git a/server/addons/cos/Cos.php b/server/addons/cos/Cos.php
new file mode 100644
index 0000000..797e9ca
--- /dev/null
+++ b/server/addons/cos/Cos.php
@@ -0,0 +1,158 @@
+addons_path . str_replace('/', DS, 'library/Qcloud/Cos/'));
+ }
+ if (!class_exists('\GuzzleHttp\Command\Command')) {
+ Loader::addNamespace('GuzzleHttp\Command', $this->addons_path . str_replace('/', DS, 'library/Guzzle/command/src/'));
+ }
+ if (!class_exists('\GuzzleHttp\Command\Guzzle\Description')) {
+ Loader::addNamespace('GuzzleHttp\Command\Guzzle', $this->addons_path . str_replace('/', DS, 'library/Guzzle/guzzle-services/src/'));
+ }
+ if (!class_exists('\GuzzleHttp\UriTemplate\UriTemplate')) {
+ Loader::addNamespace('GuzzleHttp\UriTemplate', $this->addons_path . str_replace('/', DS, 'library/Guzzle/uri-template/src/'));
+ }
+
+ }
+
+ /**
+ * 判断是否来源于API上传
+ */
+ public function moduleInit($request)
+ {
+ $config = $this->getConfig();
+ $module = strtolower($request->module());
+ if ($module == 'api' && ($config['apiupload'] ?? 0) &&
+ strtolower($request->controller()) == 'common' &&
+ strtolower($request->action()) == 'upload') {
+ request()->param('isApi', true);
+ App::invokeMethod(["\\addons\\cos\\controller\\Index", "upload"], ['isApi' => true]);
+ }
+ }
+
+ /**
+ *
+ */
+ public function uploadConfigInit(&$upload)
+ {
+ $config = $this->getConfig();
+ $module = request()->module();
+ $module = $module ? strtolower($module) : 'index';
+
+ $data = ['deadline' => time() + $config['expire']];
+ $signature = hash_hmac('sha1', json_encode($data), $config['secretKey'], true);
+
+ $token = '';
+ if (Auth::isModuleAllow()) {
+ $token = $config['appId'] . ':' . base64_encode($signature) . ':' . base64_encode(json_encode($data));
+ }
+ $multipart = [
+ 'costoken' => $token
+ ];
+
+ $upload = array_merge($upload, [
+ 'cdnurl' => $config['cdnurl'],
+ 'uploadurl' => $config['uploadmode'] == 'client' ? $config['uploadurl'] : addon_url('cos/index/upload', [], false, true),
+ 'uploadmode' => $config['uploadmode'],
+ 'bucket' => $config['bucket'],
+ 'maxsize' => $config['maxsize'],
+ 'mimetype' => $config['mimetype'],
+ 'savekey' => $config['savekey'],
+ 'chunking' => (bool)($config['chunking'] ?? $upload['chunking']),
+ 'chunksize' => (int)($config['chunksize'] ?? $upload['chunksize']),
+ 'multipart' => $multipart,
+ 'storage' => $this->getName(),
+ 'multiple' => (bool)$config['multiple'],
+ ]);
+ }
+
+ /**
+ * 附件删除后
+ */
+ public function uploadDelete($attachment)
+ {
+ $config = $this->getConfig();
+ if ($attachment['storage'] == 'cos' && isset($config['syncdelete']) && $config['syncdelete']) {
+ $cosConfig = array(
+ 'region' => $config['region'],
+ 'schema' => 'https', //协议头部,默认为http
+ 'credentials' => array(
+ 'secretId' => $config['secretId'],
+ 'secretKey' => $config['secretKey']
+ )
+ );
+ $oss = new Client($cosConfig);
+ $ret = $oss->deleteObject(array('Bucket' => $config['bucket'], 'Key' => ltrim($attachment->url, '/')));
+
+ //如果是服务端中转,还需要删除本地文件
+ //if ($config['uploadmode'] == 'server') {
+ // $filePath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
+ // if ($filePath) {
+ // @unlink($filePath);
+ // }
+ //}
+ }
+ return true;
+ }
+}
diff --git a/server/addons/cos/bootstrap.js b/server/addons/cos/bootstrap.js
new file mode 100644
index 0000000..633c920
--- /dev/null
+++ b/server/addons/cos/bootstrap.js
@@ -0,0 +1,269 @@
+if (typeof Config.upload.storage !== 'undefined' && Config.upload.storage === 'cos') {
+ require(['upload'], function (Upload) {
+ //获取文件MD5值
+ var getFileMd5 = function (file, cb) {
+ //如果savekey中未检测到md5,则无需获取文件md5,直接返回upload的uuid
+ if (!Config.upload.savekey.match(/\{(file)?md5\}/)) {
+ cb && cb(file.upload.uuid);
+ return;
+ }
+ require(['../addons/cos/js/spark'], function (SparkMD5) {
+ var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
+ chunkSize = 10 * 1024 * 1024,
+ chunks = Math.ceil(file.size / chunkSize),
+ currentChunk = 0,
+ spark = new SparkMD5.ArrayBuffer(),
+ fileReader = new FileReader();
+
+ fileReader.onload = function (e) {
+ spark.append(e.target.result);
+ currentChunk++;
+ if (currentChunk < chunks) {
+ loadNext();
+ } else {
+ cb && cb(spark.end());
+ }
+ };
+
+ fileReader.onerror = function () {
+ console.warn('文件读取错误');
+ };
+
+ function loadNext() {
+ var start = currentChunk * chunkSize,
+ end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
+
+ fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
+ }
+
+ loadNext();
+ });
+ };
+
+ var _onInit = Upload.events.onInit;
+ //初始化中完成判断
+ Upload.events.onInit = function () {
+ _onInit.apply(this, Array.prototype.slice.apply(arguments));
+ //如果上传接口不是COS,则不处理
+ if (this.options.url !== Config.upload.uploadurl) {
+ return;
+ }
+ var that = this;
+ $.extend(this.options, {
+ //关闭自动处理队列功能
+ autoQueue: false,
+ params: function (files, xhr, chunk) {
+ var params = Config.upload.multipart;
+ if (chunk) {
+ return $.extend({}, params, {
+ filesize: chunk.file.size,
+ filename: chunk.file.name,
+ chunkid: chunk.file.upload.uuid,
+ chunkindex: chunk.index,
+ chunkcount: chunk.file.upload.totalChunkCount,
+ chunkfilesize: chunk.dataBlock.data.size,
+ chunksize: that.options.chunkSize,
+ width: chunk.file.width || 0,
+ height: chunk.file.height || 0,
+ type: chunk.file.type,
+ uploadId: chunk.file.uploadId,
+ key: chunk.file.key,
+ });
+ } else {
+ params = $.extend({}, params, files[0].params);
+ params.category = files[0].category || '';
+ }
+ return params;
+ },
+ chunkSuccess: function (chunk, file, response) {
+ var etag = chunk.xhr.getResponseHeader("ETag").replace(/(^")|("$)/g, '');
+ file.etags = file.etags ? file.etags : [];
+ file.etags[chunk.index] = etag;
+ },
+ chunksUploaded: function (file, done) {
+ var that = this;
+
+ Fast.api.ajax({
+ url: "/addons/cos/index/upload",
+ data: {
+ action: 'merge',
+ filesize: file.size,
+ filename: file.name,
+ chunkid: file.upload.uuid,
+ chunkcount: file.upload.totalChunkCount,
+ md5: file.md5,
+ key: file.key,
+ uploadId: file.uploadId,
+ etags: file.etags,
+ category: file.category || '',
+ costoken: Config.upload.multipart.costoken,
+ },
+ }, function (data, ret) {
+ done(JSON.stringify(ret));
+ return false;
+ }, function (data, ret) {
+ file.accepted = false;
+ that._errorProcessing([file], ret.msg);
+ return false;
+ });
+
+ },
+ });
+
+ var _success = this.options.success;
+ //先移除已有的事件
+ this.off("success", _success).on("success", function (file, response) {
+ var ret = {code: 0, msg: response};
+ try {
+ if (response) {
+ ret = typeof response === 'string' ? JSON.parse(response) : response;
+ }
+ if (file.xhr.status === 200 || file.xhr.status === 204) {
+ if (Config.upload.uploadmode === 'client') {
+ ret = {code: 1, data: {url: '/' + file.key}};
+ }
+
+ if (ret.code == 1) {
+ var url = ret.data.url || '';
+ Fast.api.ajax({
+ url: "/addons/cos/index/notify",
+ data: {name: file.name, url: url, md5: file.md5, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: file.category || '', costoken: Config.upload.multipart.costoken}
+ }, function () {
+ return false;
+ }, function () {
+ return false;
+ });
+ } else {
+ console.error(ret);
+ }
+ } else {
+ console.error(file.xhr);
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ _success.call(this, file, ret);
+ });
+
+ this.on("addedfile", function (file) {
+ var that = this;
+ setTimeout(function () {
+ if (file.status === 'error') {
+ return;
+ }
+ getFileMd5(file, function (md5) {
+ var chunk = that.options.chunking && file.size > that.options.chunkSize ? 1 : 0;
+ var params = $(that.element).data("params") || {};
+ var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
+ category = typeof category === 'function' ? category.call(that, file) : category;
+ Fast.api.ajax({
+ url: "/addons/cos/index/params",
+ data: {method: 'POST', category: category, md5: md5, name: file.name, type: file.type, size: file.size, chunk: chunk, chunksize: that.options.chunkSize, costoken: Config.upload.multipart.costoken},
+ }, function (data) {
+ file.md5 = md5;
+ file.id = data.id;
+ file.key = data.key;
+ file.date = data.date;
+ file.uploadId = data.uploadId;
+ file.policy = data.policy;
+ file.signature = data.signature;
+ file.partsAuthorization = data.partsAuthorization;
+ file.params = data;
+ file.category = category;
+
+ if (file.status != 'error') {
+ //开始上传
+ that.enqueueFile(file);
+ } else {
+ that.removeFile(file);
+ }
+ return false;
+ }, function () {
+ that.removeFile(file);
+ });
+ });
+ }, 0);
+ });
+
+
+ if (Config.upload.uploadmode === 'client') {
+ var _method = this.options.method;
+ var _url = this.options.url;
+ this.options.method = function (files) {
+ if (files[0].upload.chunked) {
+ var chunk = null;
+ files[0].upload.chunks.forEach(function (item) {
+ if (item.status === 'uploading') {
+ chunk = item;
+ }
+ });
+ if (!chunk) {
+ return "POST";
+ } else {
+ return "PUT";
+ }
+ }
+ return _method;
+ };
+ this.options.url = function (files) {
+ if (files[0].upload.chunked) {
+ var chunk = null;
+ files[0].upload.chunks.forEach(function (item) {
+ if (item.status === 'uploading') {
+ chunk = item;
+ }
+ });
+ var index = chunk.dataBlock.chunkIndex;
+ // debugger;
+ this.options.headers = {"Authorization": files[0]['partsAuthorization'][index], "x-date": files[0]['date']};
+ if (!chunk) {
+ return Config.upload.uploadurl + "/" + files[0].key + "?uploadId=" + files[0].uploadId;
+ } else {
+ return Config.upload.uploadurl + "/" + files[0].key + "?partNumber=" + (index + 1) + "&uploadId=" + files[0].uploadId;
+ }
+ }
+ return _url;
+ };
+ this.options.params = function (files, xhr, chunk) {
+ var params = Config.upload.multipart;
+ if (chunk) {
+ return $.extend({}, params, {
+ filesize: chunk.file.size,
+ filename: chunk.file.name,
+ chunkid: chunk.file.upload.uuid,
+ chunkindex: chunk.index,
+ chunkcount: chunk.file.upload.totalChunkCount,
+ chunkfilesize: chunk.dataBlock.data.size,
+ width: chunk.file.width || 0,
+ height: chunk.file.height || 0,
+ type: chunk.file.type,
+ });
+ } else {
+ var retParams = $.extend({}, params, files[0].params || {});
+ delete retParams.costoken;
+ return retParams;
+ }
+ };
+ this.on("sending", function (file, xhr, formData) {
+ var that = this;
+ if (file.upload.chunked) {
+ var _send = xhr.send;
+ xhr.send = function () {
+ var chunk = null;
+ file.upload.chunks.forEach(function (item) {
+ if (item.status == 'uploading') {
+ chunk = item;
+ }
+ });
+ if (chunk) {
+ _send.call(xhr, chunk.dataBlock.data);
+ }
+ };
+ } else {
+
+ }
+ });
+ }
+ };
+ });
+}
diff --git a/server/addons/cos/config.php b/server/addons/cos/config.php
new file mode 100644
index 0000000..48e41e3
--- /dev/null
+++ b/server/addons/cos/config.php
@@ -0,0 +1,257 @@
+ 'appId',
+ 'title' => 'AppID',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '1234567890',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '请前往腾讯控制台 > 访问管理 > API密钥',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'secretId',
+ 'title' => 'SecretId',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '请前往腾讯控制台 > 访问管理 > API密钥',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'secretKey',
+ 'title' => 'SecretKey',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '请前往腾讯控制台 > 访问管理 > API密钥',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'bucket',
+ 'title' => '存储桶名称',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => 'yourbucket-1234567890',
+ 'rule' => 'required;bucket',
+ 'msg' => '',
+ 'tip' => '存储空间名称',
+ 'ok' => '',
+ 'extend' => 'data-rule-bucket="[/^[0-9a-z_\-]{3,63}$/, \'请输入正确的存储桶名称\']"',
+ ],
+ [
+ 'name' => 'region',
+ 'title' => '地域名称',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => 'ap-guangzhou',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '请输入地域简称,请注意使用英文',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'uploadmode',
+ 'title' => '上传模式',
+ 'type' => 'select',
+ 'content' => [
+ 'client' => '客户端直传(速度快,无备份)',
+ 'server' => '服务器中转(占用服务器带宽,有备份)',
+ ],
+ 'value' => 'server',
+ 'rule' => '',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'serverbackup',
+ 'title' => '服务器中转模式备份',
+ 'type' => 'radio',
+ 'content' => [
+ 1 => '备份(附件管理将产生2条记录)',
+ 0 => '不备份',
+ ],
+ 'value' => '1',
+ 'rule' => '',
+ 'msg' => '',
+ 'tip' => '服务器中转模式下是否备份文件',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'uploadurl',
+ 'title' => '上传接口地址',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '',
+ 'rule' => 'required;uploadurl',
+ 'msg' => '',
+ 'tip' => '请输入你的上传接口地址',
+ 'ok' => '',
+ 'extend' => 'data-rule-uploadurl="[/^http(s)?:\/\/.*$/, \'必需以http(s)://开头\']"',
+ ],
+ [
+ 'name' => 'cdnurl',
+ 'title' => 'CDN地址',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '',
+ 'rule' => 'required;cdnurl',
+ 'msg' => '',
+ 'tip' => '请配置你的CDN地址或在存储桶基础配置中获取',
+ 'ok' => '',
+ 'extend' => 'data-rule-cdnurl="[/^http(s)?:\/\/.*$/, \'必需以http(s)://开头\']"',
+ ],
+ [
+ 'name' => 'savekey',
+ 'title' => '保存文件名',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '/{category}/{year}{mon}{day}/{filemd5}{.suffix}',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'expire',
+ 'title' => '上传有效时长',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '600',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '用户停留页面上传有效时长,单位秒',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'maxsize',
+ 'title' => '最大可上传',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '10M',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'mimetype',
+ 'title' => '可上传后缀格式',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => 'jpg,png,bmp,jpeg,gif,zip,rar',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'multiple',
+ 'title' => '多文件上传',
+ 'type' => 'bool',
+ 'content' => [],
+ 'value' => '0',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'thumbstyle',
+ 'title' => '缩略图样式',
+ 'type' => 'string',
+ 'content' => [],
+ 'value' => '',
+ 'rule' => '',
+ 'msg' => '',
+ 'tip' => '用于后台列表缩略图样式,可使用:?imageMogr2/thumbnail/120x90',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'chunking',
+ 'title' => '分片上传',
+ 'type' => 'radio',
+ 'content' => [
+ 1 => '开启',
+ 0 => '关闭',
+ ],
+ 'value' => '0',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'chunksize',
+ 'title' => '分片大小',
+ 'type' => 'number',
+ 'content' => [],
+ 'value' => '4194304',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'syncdelete',
+ 'title' => '附件删除时是否同步删除云存储文件',
+ 'type' => 'bool',
+ 'content' => [],
+ 'value' => '0',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'apiupload',
+ 'title' => 'API接口使用云存储',
+ 'type' => 'bool',
+ 'content' => [],
+ 'value' => '0',
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'noneedlogin',
+ 'title' => '免登录上传',
+ 'type' => 'checkbox',
+ 'content' => [
+ 'api' => 'API',
+ 'index' => '前台',
+ 'admin' => '后台',
+ ],
+ 'value' => '',
+ 'rule' => '',
+ 'msg' => '',
+ 'tip' => '',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+];
diff --git a/server/addons/cos/controller/Index.php b/server/addons/cos/controller/Index.php
new file mode 100644
index 0000000..da166ea
--- /dev/null
+++ b/server/addons/cos/controller/Index.php
@@ -0,0 +1,377 @@
+cosConfig = array(
+ 'region' => $config['region'],
+ 'schema' => 'https', //协议头部,默认为http
+ 'credentials' => array(
+ 'secretId' => $config['secretId'],
+ 'secretKey' => $config['secretKey']
+ )
+ );
+ }
+
+ public function index()
+ {
+ Config::set('default_return_type', 'html');
+ $this->error("当前插件暂无前台页面");
+ }
+
+ public function params()
+ {
+ $this->check();
+
+ $config = get_addon_config('cos');
+ $name = $this->request->post('name');
+ $md5 = $this->request->post('md5');
+ $chunk = $this->request->post('chunk');
+ $name = xss_clean($name);
+
+ // 检查文件后缀
+ $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
+ $allowedExtensions = explode(',', strtolower($config['mimetype']));
+ if (!in_array($extension, $allowedExtensions) || in_array($extension, ['php', 'html', 'htm', 'phar', 'phtml']) || preg_match("/^php(.*)/i", $extension)) {
+ $this->error('不允许的文件类型');
+ }
+
+ // 检查文件大小
+ $fileSize = $this->request->post('size');
+ preg_match('/([0-9\.]+)(\w+)/', $config['maxsize'], $matches);
+ $configMaxSize = $matches ? $matches[1] : $config['maxsize'];
+ $type = $matches ? strtolower($matches[2]) : 'b';
+ $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
+ $maxSize = (int)($configMaxSize * pow(1024, $typeDict[$type] ?? 0));
+ if ($fileSize > $maxSize) {
+ $this->error('文件大小超出限制');
+ }
+
+ $key = (new Upload())->getSavekey($config['savekey'], $name, $md5);
+ $key = ltrim($key, "/");
+ $params = [
+ 'key' => $key,
+ 'md5' => $md5
+ ];
+ if ($chunk) {
+ $fileSize = $this->request->post('size');
+ $oss = new Client($this->cosConfig);
+
+ $result = $oss->createMultipartUpload(array(
+ 'Bucket' => $config['bucket'],
+ 'Key' => $key,
+ ));
+ $uploadId = $result['UploadId'];
+
+ $sig = new Signature($config['secretId'], $config['secretKey'], ['signHost' => true]);
+
+ $partSize = $this->request->post("chunksize");
+ $i = 0;
+ $size_count = $fileSize;
+ $values = array();
+ while ($size_count > 0) {
+ $size_count -= $partSize;
+ $values[] = array(
+ $partSize * $i,
+ ($size_count > 0) ? $partSize : ($size_count + $partSize),
+ );
+ $i++;
+ }
+
+ $params['key'] = $key;
+ $params['uploadId'] = $uploadId;
+ $params['partsAuthorization'] = [];
+ $date = gmdate('D, d M Y H:i:s \G\M\T');
+ foreach ($values as $index => $part) {
+ $partNumber = $index + 1;
+ $options = array(
+ 'Bucket' => $config['bucket'],
+ 'Key' => $key,
+ 'UploadId' => $uploadId,
+ 'PartNumber' => $partNumber,
+ 'Body' => ''
+ );
+ $command = $oss->getCommand('uploadPart', $options);
+ $request = $oss->commandToRequestTransformer($command);
+ $authorization = $sig->createAuthorization($request);
+
+ $params['partsAuthorization'][$index] = $authorization;
+ }
+ $params['date'] = $date;
+ } else {
+ if ($config['uploadmode'] == 'client') {
+ $expiretime = time() + $config['expire'];
+ $expiration = gmdate("Y-m-d\TH:i:s.414\Z", $expiretime);
+ $keytime = (time() - 60) . ';' . $expiretime;
+ $policy = json_encode([
+ 'expiration' => $expiration,
+ 'conditions' => [
+ ['q-sign-algorithm' => 'sha1'],
+ ['q-ak' => $config['secretId']],
+ ['q-sign-time' => $keytime],
+ ['content-length-range', 1, $maxSize]
+ ]
+ ]);
+ $signature = hash_hmac('sha1', sha1($policy), hash_hmac('sha1', $keytime, $config['secretKey']));
+ $params = [
+ 'key' => $key,
+ 'policy' => base64_encode($policy),
+ 'q-sign-algorithm' => 'sha1',
+ 'q-ak' => $config['secretId'],
+ 'q-key-time' => $keytime,
+ 'q-sign-time' => $keytime,
+ 'q-signature' => $signature
+ ];
+ }
+ }
+
+ $this->success('', null, $params);
+ }
+
+ /**
+ * 服务器中转上传文件
+ * 上传分片
+ * 合并分片
+ * @param bool $isApi
+ */
+ public function upload($isApi = false)
+ {
+ if ($isApi === true) {
+ if (!Auth::isModuleAllow()) {
+ $this->error("请登录后再进行操作");
+ }
+ } else {
+ $this->check();
+ }
+ $config = get_addon_config('cos');
+ $oss = new Client($this->cosConfig);
+
+ //检测删除文件或附件
+ $checkDeleteFile = function ($attachment, $upload, $force = false) use ($config) {
+ //如果设定为不备份则删除文件和记录 或 强制删除
+ if ((isset($config['serverbackup']) && !$config['serverbackup']) || $force) {
+ if ($attachment && !empty($attachment['id'])) {
+ $attachment->delete();
+ }
+ if ($upload) {
+ //文件绝对路径
+ $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
+ @unlink($filePath);
+ }
+ }
+ };
+
+ $chunkid = $this->request->post("chunkid");
+ if ($chunkid) {
+ $action = $this->request->post("action");
+ $chunkindex = $this->request->post("chunkindex/d");
+ $chunkcount = $this->request->post("chunkcount/d");
+ $filesize = $this->request->post("filesize");
+ $filename = $this->request->post("filename");
+ $method = $this->request->method(true);
+ $key = $this->request->post("key");
+ $uploadId = $this->request->post("uploadId");
+
+ if ($action == 'merge') {
+ $attachment = null;
+ $upload = null;
+ //合并分片
+ if ($config['uploadmode'] == 'server') {
+ //合并分片文件
+ try {
+ $upload = new Upload();
+ $attachment = $upload->merge($chunkid, $chunkcount, $filename);
+ } catch (UploadException $e) {
+ $this->error($e->getMessage());
+ }
+ }
+
+ $etags = $this->request->post("etags/a", []);
+ if (count($etags) != $chunkcount) {
+ $checkDeleteFile($attachment, $upload, true);
+ $this->error("分片数据错误");
+ }
+ $listParts = [];
+ for ($i = 0; $i < $chunkcount; $i++) {
+ $listParts[] = array("PartNumber" => $i + 1, "ETag" => $etags[$i]);
+ }
+ try {
+ $result = $oss->completeMultipartUpload(
+ array(
+ 'Bucket' => $config['bucket'],
+ 'Key' => $key,
+ 'UploadId' => $uploadId,
+ 'Parts' => $listParts
+ )
+ );
+ } catch (\Exception $e) {
+ $checkDeleteFile($attachment, $upload, true);
+ $this->error($e->getMessage());
+ }
+
+ if (!isset($result['Key'])) {
+ $checkDeleteFile($attachment, $upload, true);
+ $this->error("上传失败(1001)");
+ } else {
+ $checkDeleteFile($attachment, $upload);
+ $this->success("上传成功", '', ['url' => "/" . $key, 'fullurl' => cdnurl("/" . $key, true)]);
+ }
+ } else {
+ //默认普通上传文件
+ $file = $this->request->file('file');
+ try {
+ $upload = new Upload($file);
+ $file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
+ } catch (UploadException $e) {
+ $this->error($e->getMessage());
+ }
+ try {
+ $params = array(
+ 'Bucket' => $config['bucket'],
+ 'Key' => $key,
+ 'UploadId' => $uploadId,
+ 'PartNumber' => $chunkindex + 1,
+ 'Body' => $file->fread($file->getSize())
+ );
+ $ret = $oss->uploadPart($params);
+ $etag = $ret['ETag'];
+ } catch (\Exception $e) {
+ $this->error($e->getMessage());
+ }
+
+ $this->success("上传成功", "", [], 3, ['ETag' => $etag]);
+ }
+ } else {
+ $attachment = null;
+ //默认普通上传文件
+ $file = $this->request->file('file');
+ try {
+ $upload = new Upload($file);
+ $attachment = $upload->upload();
+ } catch (UploadException $e) {
+ $this->error($e->getMessage());
+ }
+
+ //文件绝对路径
+ $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
+
+ $url = $attachment->url;
+
+ try {
+ $ret = $oss->upload($config['bucket'], ltrim($attachment->url, "/"), $upload->getFile());
+ //成功不做任何操作
+ } catch (\Exception $e) {
+ $checkDeleteFile($attachment, $upload, true);
+ \think\Log::write($e->getMessage());
+ $this->error("上传失败(1002)");
+ }
+ $checkDeleteFile($attachment, $upload);
+
+ // 记录云存储记录
+ $data = $attachment->toArray();
+ unset($data['id']);
+ $data['storage'] = 'cos';
+ Attachment::create($data, true);
+
+ $this->success("上传成功", '', ['url' => $url, 'fullurl' => cdnurl($url, true)]);
+ }
+ return;
+ }
+
+ /**
+ * 回调
+ */
+ public function notify()
+ {
+ $this->check();
+ $config = get_addon_config('cos');
+ if ($config['uploadmode'] != 'client') {
+ $this->error("无需执行该操作");
+ }
+ $this->request->filter('trim,strip_tags,htmlspecialchars,xss_clean');
+
+ $size = $this->request->post('size/d');
+ $name = $this->request->post('name', '');
+ $md5 = $this->request->post('md5', '');
+ $type = $this->request->post('type', '');
+ $url = $this->request->post('url', '');
+ $width = $this->request->post('width/d');
+ $height = $this->request->post('height/d');
+ $category = $this->request->post('category', '');
+ $category = array_key_exists($category, config('site.attachmentcategory') ?? []) ? $category : '';
+ $suffix = strtolower(pathinfo($name, PATHINFO_EXTENSION));
+ $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
+ $attachment = Attachment::where('url', $url)->where('storage', 'cos')->find();
+ if (!$attachment) {
+ $params = array(
+ 'category' => $category,
+ 'admin_id' => (int)session('admin.id'),
+ 'user_id' => (int)$this->auth->id,
+ 'filesize' => $size,
+ 'filename' => $name,
+ 'imagewidth' => $width,
+ 'imageheight' => $height,
+ 'imagetype' => $suffix,
+ 'imageframes' => 0,
+ 'mimetype' => $type,
+ 'url' => $url,
+ 'uploadtime' => time(),
+ 'storage' => 'cos',
+ 'sha1' => $md5,
+ );
+ Attachment::create($params, true);
+ }
+ $this->success();
+ }
+
+ /**
+ * 检查签名是否正确或过期
+ */
+ protected function check()
+ {
+ $costoken = $this->request->post('costoken', '', 'trim');
+ if (!$costoken) {
+ $this->error("参数不正确(code:1)");
+ }
+ $config = get_addon_config('cos');
+ list($appId, $sign, $data) = explode(':', $costoken);
+ if (!$appId || !$sign || !$data) {
+ $this->error("参数不正确(code:2)");
+ }
+ if ($appId !== $config['appId']) {
+ $this->error("参数不正确(code:3)");
+ }
+ if ($sign !== base64_encode(hash_hmac('sha1', base64_decode($data), $config['secretKey'], true))) {
+ $this->error("签名不正确");
+ }
+ $json = json_decode(base64_decode($data), true);
+ if ($json['deadline'] < time()) {
+ $this->error("请求已经超时");
+ }
+ }
+}
diff --git a/server/addons/cos/info.ini b/server/addons/cos/info.ini
new file mode 100644
index 0000000..1b38d94
--- /dev/null
+++ b/server/addons/cos/info.ini
@@ -0,0 +1,10 @@
+name = cos
+title = 腾讯COS云存储
+intro = 使用腾讯COS云存储作为默认云储存
+author = FastAdmin
+website =
+version = 1.2.12
+state = 1
+url = /addons/cos
+license = extended
+licenseto = 26425
diff --git a/server/addons/cos/library/Auth.php b/server/addons/cos/library/Auth.php
new file mode 100644
index 0000000..3aaf5f3
--- /dev/null
+++ b/server/addons/cos/library/Auth.php
@@ -0,0 +1,37 @@
+module();
+ $module = $module ? strtolower($module) : 'index';
+ $noNeedLogin = array_filter(explode(',', $config['noneedlogin'] ?? ''));
+ $isModuleLogin = false;
+ $tagName = 'upload_config_checklogin';
+ foreach (\think\Hook::get($tagName) as $index => $name) {
+ if (\think\Hook::exec($name, $tagName)) {
+ $isModuleLogin = true;
+ break;
+ }
+ }
+ if (in_array($module, $noNeedLogin)
+ || ($module == 'admin' && \app\admin\library\Auth::instance()->id)
+ || ($module != 'admin' && \app\common\library\Auth::instance()->id)
+ || $isModuleLogin) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/server/addons/cos/library/Guzzle/command/CHANGELOG.md b/server/addons/cos/library/Guzzle/command/CHANGELOG.md
new file mode 100644
index 0000000..4ca26ab
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/CHANGELOG.md
@@ -0,0 +1,60 @@
+# CHANGELOG
+
+## 1.0.0 - 2016-11-24
+
+* Add badges to README.md
+* Switch README from .rst to .md format
+* Update dependencies
+* Add command to handler call to provide support for GuzzleServices
+
+## 0.9.0 - 2016-01-30
+
+* Updated to use Guzzle 6 and PSR-7.
+* Event system has been replaced with a middleware system
+ * Middleware at the command layer work the same as middleware from the
+ HTTP layer, but work with `Command` and `Result` objects instead of
+ `Request` and `Response` objects
+ * The command middleware is in a separate `HandlerStack` instance than the
+ HTTP middleware.
+* `Result` objects are the result of executing a `Command` and are used to hold
+ the parsed response data.
+* Asynchronous code now uses the `guzzlehttp/promises` package instead of
+ `guzzlehttp/ringphp`, which means that asynchronous results are implemented
+ as Promises/A+ compliant `Promise` objects, instead of futures.
+* The existing `Subscriber`s were removed.
+* The `ServiceClientInterface` and `ServiceClient` class now provide the basic
+ foundation of a web service client.
+
+## 0.8.0 - 2015-02-02
+
+* Removed `setConfig` from `ServiceClientInterface`.
+* Added `initTransaction` to `ServiceClientInterface`.
+
+## 0.7.1 - 2015-01-14
+
+* Fixed and issue where intercepting commands encapsulated by a
+ CommandToRequestIterator could lead to deep recursion. These commands are
+ now skipped and the iterator moves to the next element using a `goto`
+ statement.
+
+## 0.7.0 - 2014-10-12
+
+* Updated to use Guzzle 5, and added support for asynchronous results.
+* Renamed `prepare` event to `prepared`.
+* Added `init` event.
+
+## 0.6.0 - 2014-08-08
+
+* Added a Debug subscriber that can be used to trace through the lifecycle of
+ a command and how it is modified in each event.
+
+## 0.5.0 - 2014-08-01
+
+* Rewrote event system so that all exceptions encountered during the transfer
+ of a command are emitted to the "error" event.
+* No longer wrapping exceptions thrown during the execution of a command.
+* Added the ability to get a CommandTransaction from events and updating
+ classes to use a CommandTransaction rather than many constructor arguments.
+* Fixed an issue with sending many commands in parallel
+* Added `batch()` to ServiceClientInterface for sending commands in batches
+* Added subscriber to easily mock commands results
diff --git a/server/addons/cos/library/Guzzle/command/LICENSE b/server/addons/cos/library/Guzzle/command/LICENSE
new file mode 100644
index 0000000..0d4b6ca
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Michael Dowling
+Copyright (c) 2014 Graham Campbell
+Copyright (c) 2014 Jeremy Lindblom
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/server/addons/cos/library/Guzzle/command/Makefile b/server/addons/cos/library/Guzzle/command/Makefile
new file mode 100644
index 0000000..762a2ac
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/Makefile
@@ -0,0 +1,2 @@
+test:
+ vendor/bin/phpunit $(TEST)
diff --git a/server/addons/cos/library/Guzzle/command/README.md b/server/addons/cos/library/Guzzle/command/README.md
new file mode 100644
index 0000000..14e490e
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/README.md
@@ -0,0 +1,168 @@
+# Guzzle Commands
+
+This library uses Guzzle and provides the foundations to create fully-featured
+web service clients by abstracting Guzzle HTTP *requests* and *responses* into
+higher-level *commands* and *results*. A *middleware* system, analogous to, but
+separate from, the one in the HTTP layer may be used to customize client
+behavior when preparing commands into requests and processing responses into
+results.
+
+### Commands
+
+Key-value pair objects representing an operation of a web service. Commands
+have a name and a set of parameters.
+
+### Results
+
+Key-value pair objects representing the processed result of executing an
+operation of a web service.
+
+## Installing
+
+This project can be installed using [Composer](https://getcomposer.org/):
+
+```
+composer require guzzlehttp/command
+```
+
+## Service Clients
+
+Service Clients are web service clients that implement the
+`GuzzleHttp\Command\ServiceClientInterface` and use an underlying Guzzle HTTP
+client (`GuzzleHttp\ClientInterface`) to communicate with the service. Service
+clients create and execute *commands* (`GuzzleHttp\Command\CommandInterface`),
+which encapsulate operations within the web service, including the operation
+name and parameters. This library provides a generic implementation of a service
+client: the `GuzzleHttp\Command\ServiceClient` class.
+
+## Instantiating a Service Client
+
+The provided service client implementation (`GuzzleHttp\Command\ServiceClient`)
+can be instantiated by providing the following arguments:
+
+1. A fully-configured Guzzle HTTP client that will be used to perform the
+ underlying HTTP requests. That is, an instance of an object implementing
+ `GuzzleHttp\ClientInterface` such as `new GuzzleHttp\Client()`.
+1. A callable that transforms a Command into a Request. The function should
+ accept a `GuzzleHttp\Command\CommandInterface` object and return a
+ `Psr\Http\Message\RequestInterface` object.
+1. A callable that transforms a Response into a Result. The function should
+ accept a `Psr\Http\Message\ResponseInterface` object and optionally a
+ `Psr\Http\Message\RequestInterface` object, and return a
+ `GuzzleHttp\Command\ResultInterface` object.
+1. Optionally, a Guzzle HandlerStack (`GuzzleHttp\HandlerStack`), which can be
+ used to add command-level middleware to the service client.
+
+Below is an example configured to send and receive JSON payloads:
+
+```php
+use GuzzleHttp\Command\CommandInterface;
+use GuzzleHttp\Command\Result;
+use GuzzleHttp\Command\ResultInterface;
+use GuzzleHttp\Command\ServiceClient;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\UriTemplate\UriTemplate;
+use GuzzleHttp\Utils;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+$client = new ServiceClient(
+ new HttpClient(),
+ function (CommandInterface $command): RequestInterface {
+ return new Request(
+ 'POST',
+ UriTemplate::expand('/{command}', ['command' => $command->getName()]),
+ ['Accept' => 'application/json', 'Content-Type' => 'application/json'],
+ Utils::jsonEncode($command->toArray())
+ );
+ },
+ function (ResponseInterface $response, RequestInterface $request): ResultInterface {
+ return new Result(
+ Utils::jsonDecode((string) $response->getBody(), true)
+ );
+ }
+);
+```
+
+## Executing Commands
+
+Service clients create command objects using the ``getCommand()`` method.
+
+```php
+$commandName = 'foo';
+$arguments = ['baz' => 'bar'];
+$command = $client->getCommand($commandName, $arguments);
+```
+
+After creating a command, you may execute the command using the `execute()`
+method of the client.
+
+```php
+$result = $client->execute($command);
+```
+
+The result of executing a command will be an instance of an object implementing
+`GuzzleHttp\Command\ResultInterface`. Result objects are `ArrayAccess`-ible and
+contain the data parsed from HTTP response.
+
+Service clients have magic methods that act as shortcuts to executing commands
+by name without having to create the ``Command`` object in a separate step
+before executing it.
+
+```php
+$result = $client->foo(['baz' => 'bar']);
+```
+
+## Asynchronous Commands
+
+@TODO Add documentation
+
+* ``-Async`` suffix for client methods
+* Promises
+
+```php
+// Create and execute an asynchronous command.
+$command = $command = $client->getCommand('foo', ['baz' => 'bar']);
+$promise = $client->executeAsync($command);
+
+// Use asynchronous commands with magic methods.
+$promise = $client->fooAsync(['baz' => 'bar']);
+```
+
+@TODO Add documentation
+
+* ``wait()``-ing on promises.
+
+```php
+$result = $promise->wait();
+
+echo $result['fizz']; //> 'buzz'
+```
+
+## Concurrent Requests
+
+@TODO Add documentation
+
+* ``executeAll()``
+* ``executeAllAsync()``.
+* Options (``fulfilled``, ``rejected``, ``concurrency``)
+
+## Middleware: Extending the Client
+
+Middleware can be added to the service client or underlying HTTP client to
+implement additional behavior and customize the ``Command``-to-``Result`` and
+``Request``-to-``Response`` lifecycles, respectively.
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/command/security/policy) for more information.
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-command?utm_source=packagist-guzzlehttp-command&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/server/addons/cos/library/Guzzle/command/composer.json b/server/addons/cos/library/Guzzle/command/composer.json
new file mode 100644
index 0000000..704afa6
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/composer.json
@@ -0,0 +1,50 @@
+{
+ "name": "guzzlehttp/command",
+ "description": "Provides the foundation for building command-based web service clients",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "guzzlehttp/guzzle": "^7.4.1",
+ "guzzlehttp/promises": "^1.5.1",
+ "guzzlehttp/psr7": "^1.8.3 || ^2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.19"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Command\\": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "config": {
+ "preferred-install": "dist",
+ "sort-packages": true
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/phpunit.xml.dist b/server/addons/cos/library/Guzzle/command/phpunit.xml.dist
new file mode 100644
index 0000000..59ce4a2
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/phpunit.xml.dist
@@ -0,0 +1,23 @@
+
+
+
+
+ tests
+
+
+
+
+ src
+
+
+
diff --git a/server/addons/cos/library/Guzzle/command/src/Command.php b/server/addons/cos/library/Guzzle/command/src/Command.php
new file mode 100644
index 0000000..cff70a2
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/Command.php
@@ -0,0 +1,55 @@
+name = $name;
+ $this->data = $args;
+ $this->handlerStack = $handlerStack;
+ }
+
+ public function getHandlerStack()
+ {
+ return $this->handlerStack;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function hasParam($name)
+ {
+ return array_key_exists($name, $this->data);
+ }
+
+ public function __clone()
+ {
+ if ($this->handlerStack) {
+ $this->handlerStack = clone $this->handlerStack;
+ }
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/src/CommandInterface.php b/server/addons/cos/library/Guzzle/command/src/CommandInterface.php
new file mode 100644
index 0000000..5d23c99
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/CommandInterface.php
@@ -0,0 +1,39 @@
+getCommand()) {
+ return $prev;
+ }
+
+ // If the exception is a RequestException, get the Request and Response.
+ $request = $response = null;
+ if ($prev instanceof RequestException) {
+ $request = $prev->getRequest();
+ $response = $prev->getResponse();
+ }
+
+ // Throw a more specific exception for 4XX or 5XX responses.
+ $class = self::class;
+ $statusCode = $response ? $response->getStatusCode() : 0;
+ if ($statusCode >= 400 && $statusCode < 500) {
+ $class = CommandClientException::class;
+ } elseif ($statusCode >= 500 && $statusCode < 600) {
+ $class = CommandServerException::class;
+ }
+
+ // Prepare the message.
+ $message = 'There was an error executing the ' . $command->getName()
+ . ' command: ' . $prev->getMessage();
+
+ // Create the exception.
+ return new $class($message, $command, $prev, $request, $response);
+ }
+
+ /**
+ * @param string $message Exception message
+ * @param CommandInterface $command
+ * @param \Exception $previous Previous exception (if any)
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ */
+ public function __construct(
+ $message,
+ CommandInterface $command,
+ \Exception $previous = null,
+ RequestInterface $request = null,
+ ResponseInterface $response = null
+ ) {
+ $this->command = $command;
+ $this->request = $request;
+ $this->response = $response;
+ parent::__construct($message, 0, $previous);
+ }
+
+ /**
+ * Gets the command that failed.
+ *
+ * @return CommandInterface
+ */
+ public function getCommand()
+ {
+ return $this->command;
+ }
+
+ /**
+ * Gets the request that caused the exception
+ *
+ * @return RequestInterface|null
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * Gets the associated response
+ *
+ * @return ResponseInterface|null
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/src/Exception/CommandServerException.php b/server/addons/cos/library/Guzzle/command/src/Exception/CommandServerException.php
new file mode 100644
index 0000000..22356b5
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/Exception/CommandServerException.php
@@ -0,0 +1,7 @@
+data;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function offsetExists($offset)
+ {
+ return array_key_exists($offset, $this->data);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return isset($this->data[$offset]) ? $this->data[$offset] : null;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value)
+ {
+ $this->data[$offset] = $value;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset)
+ {
+ unset($this->data[$offset]);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->data);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->data);
+ }
+
+ public function toArray()
+ {
+ return $this->data;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/src/Result.php b/server/addons/cos/library/Guzzle/command/src/Result.php
new file mode 100644
index 0000000..3041caf
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/Result.php
@@ -0,0 +1,18 @@
+data = $data;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/src/ResultInterface.php b/server/addons/cos/library/Guzzle/command/src/ResultInterface.php
new file mode 100644
index 0000000..4ae49a8
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/ResultInterface.php
@@ -0,0 +1,9 @@
+httpClient = $httpClient;
+ $this->commandToRequestTransformer = $commandToRequestTransformer;
+ $this->responseToResultTransformer = $responseToResultTransformer;
+ $this->handlerStack = $commandHandlerStack ?: new HandlerStack();
+ $this->handlerStack->setHandler($this->createCommandHandler());
+ }
+
+ public function getHttpClient()
+ {
+ return $this->httpClient;
+ }
+
+ public function getHandlerStack()
+ {
+ return $this->handlerStack;
+ }
+
+ public function getCommand($name, array $params = [])
+ {
+ return new Command($name, $params, clone $this->handlerStack);
+ }
+
+ public function execute(CommandInterface $command)
+ {
+ return $this->executeAsync($command)->wait();
+ }
+
+ public function executeAsync(CommandInterface $command)
+ {
+ $stack = $command->getHandlerStack() ?: $this->handlerStack;
+ $handler = $stack->resolve();
+
+ return $handler($command);
+ }
+
+ public function executeAll($commands, array $options = [])
+ {
+ // Modify provided callbacks to track results.
+ $results = [];
+ $options['fulfilled'] = function ($v, $k) use (&$results, $options) {
+ if (isset($options['fulfilled'])) {
+ $options['fulfilled']($v, $k);
+ }
+ $results[$k] = $v;
+ };
+ $options['rejected'] = function ($v, $k) use (&$results, $options) {
+ if (isset($options['rejected'])) {
+ $options['rejected']($v, $k);
+ }
+ $results[$k] = $v;
+ };
+
+ // Execute multiple commands synchronously, then sort and return the results.
+ return $this->executeAllAsync($commands, $options)
+ ->then(function () use (&$results) {
+ ksort($results);
+ return $results;
+ })
+ ->wait();
+ }
+
+ public function executeAllAsync($commands, array $options = [])
+ {
+ // Apply default concurrency.
+ if (!isset($options['concurrency'])) {
+ $options['concurrency'] = 25;
+ }
+
+ // Convert the iterator of commands to a generator of promises.
+ $commands = Create::iterFor($commands);
+ $promises = function () use ($commands) {
+ foreach ($commands as $key => $command) {
+ if (!$command instanceof CommandInterface) {
+ throw new \InvalidArgumentException('The iterator must '
+ . 'yield instances of ' . CommandInterface::class);
+ }
+ yield $key => $this->executeAsync($command);
+ }
+ };
+
+ // Execute the commands using a pool.
+ return (new Promise\EachPromise($promises(), $options))->promise();
+ }
+
+ /**
+ * Creates and executes a command for an operation by name.
+ *
+ * @param string $name Name of the command to execute.
+ * @param array $args Arguments to pass to the getCommand method.
+ *
+ * @return ResultInterface|PromiseInterface
+ * @see \GuzzleHttp\Command\ServiceClientInterface::getCommand
+ */
+ public function __call($name, array $args)
+ {
+ $args = isset($args[0]) ? $args[0] : [];
+ if (substr($name, -5) === 'Async') {
+ $command = $this->getCommand(substr($name, 0, -5), $args);
+ return $this->executeAsync($command);
+ } else {
+ return $this->execute($this->getCommand($name, $args));
+ }
+ }
+
+ /**
+ * Defines the main handler for commands that uses the HTTP client.
+ *
+ * @return callable
+ */
+ private function createCommandHandler()
+ {
+ return function (CommandInterface $command) {
+ return Coroutine::of(function () use ($command) {
+ // Prepare the HTTP options.
+ $opts = $command['@http'] ?: [];
+ unset($command['@http']);
+
+ try {
+ // Prepare the request from the command and send it.
+ $request = $this->transformCommandToRequest($command);
+ $promise = $this->httpClient->sendAsync($request, $opts);
+
+ // Create a result from the response.
+ $response = (yield $promise);
+ yield $this->transformResponseToResult($response, $request, $command);
+ } catch (\Exception $e) {
+ throw CommandException::fromPrevious($command, $e);
+ }
+ });
+ };
+ }
+
+ /**
+ * Transforms a Command object into a Request object.
+ *
+ * @param CommandInterface $command
+ * @return RequestInterface
+ */
+ private function transformCommandToRequest(CommandInterface $command)
+ {
+ $transform = $this->commandToRequestTransformer;
+
+ return $transform($command);
+ }
+
+
+ /**
+ * Transforms a Response object, also using data from the Request object,
+ * into a Result object.
+ *
+ * @param ResponseInterface $response
+ * @param RequestInterface $request
+ * @param CommandInterface $command
+ * @return ResultInterface
+ */
+ private function transformResponseToResult(
+ ResponseInterface $response,
+ RequestInterface $request,
+ CommandInterface $command
+ ) {
+ $transform = $this->responseToResultTransformer;
+
+ return $transform($response, $request, $command);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/command/src/ServiceClientInterface.php b/server/addons/cos/library/Guzzle/command/src/ServiceClientInterface.php
new file mode 100644
index 0000000..1f41837
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/command/src/ServiceClientInterface.php
@@ -0,0 +1,92 @@
+getConfig\('defaults'\) [\#84](https://github.com/guzzle/guzzle-services/pull/84) ([fuhry](https://github.com/fuhry))
+
+- Fixing issue \#82 to address regression for handling elements with the sa... [\#83](https://github.com/guzzle/guzzle-services/pull/83) ([sprak3000](https://github.com/sprak3000))
+
+- Fix for specified property but no value in json \(notice for undefined in... [\#76](https://github.com/guzzle/guzzle-services/pull/76) ([rfink](https://github.com/rfink))
+
+- Add ErrorHandler subscriber [\#67](https://github.com/guzzle/guzzle-services/pull/67) ([bakura10](https://github.com/bakura10))
+
+- Fix combine base url and command uri [\#108](https://github.com/guzzle/guzzle-services/pull/108) ([vlastv](https://github.com/vlastv))
+
+- Fixing JsonLocation::visit\(\) not returning a request \#106 [\#107](https://github.com/guzzle/guzzle-services/pull/107) ([Pinolo](https://github.com/Pinolo))
+
+- Fix call to undefined method "GuzzleHttp\Psr7\Uri::combine" [\#105](https://github.com/guzzle/guzzle-services/pull/105) ([horrorin](https://github.com/horrorin))
+
+- fix description for get request example [\#87](https://github.com/guzzle/guzzle-services/pull/87) ([snoek09](https://github.com/snoek09))
+
+- Allow raw values \(non array/object\) for root model definitions [\#74](https://github.com/guzzle/guzzle-services/pull/74) ([rfink](https://github.com/rfink))
+
+- Allow shortened definition of properties by assigning them directly to a type [\#72](https://github.com/guzzle/guzzle-services/pull/72) ([rfink](https://github.com/rfink))
+
+## [0.5.0](https://github.com/guzzle/guzzle-services/tree/0.5.0) (2014-12-23)
+
+[Full Changelog](https://github.com/guzzle/guzzle-services/compare/0.4.0...0.5.0)
+
+**Closed issues:**
+
+- Does it supports custom class instantiate to define an operation using a service description [\#62](https://github.com/guzzle/guzzle-services/issues/62)
+
+- Tag version 0.4.0 [\#61](https://github.com/guzzle/guzzle-services/issues/61)
+
+- XmlLocation not adding attributes to non-leaf child nodes [\#52](https://github.com/guzzle/guzzle-services/issues/52)
+
+- XmlLocation response not handling multiple tags of the same name correctly [\#51](https://github.com/guzzle/guzzle-services/issues/51)
+
+- Validation Bug [\#47](https://github.com/guzzle/guzzle-services/issues/47)
+
+- CommandException doesn't contain response data [\#44](https://github.com/guzzle/guzzle-services/issues/44)
+
+- \[Fix included\] XmlLocation requires text value to have attributes [\#37](https://github.com/guzzle/guzzle-services/issues/37)
+
+- Question: Mocking a Response does not throw exception [\#35](https://github.com/guzzle/guzzle-services/issues/35)
+
+- allow default 'location' on Model [\#26](https://github.com/guzzle/guzzle-services/issues/26)
+
+- create mock subscriber requests from descriptions [\#25](https://github.com/guzzle/guzzle-services/issues/25)
+
+**Merged pull requests:**
+
+- Documentation: Add 'boolean-string' as a supported "format" value [\#63](https://github.com/guzzle/guzzle-services/pull/63) ([jwcobb](https://github.com/jwcobb))
+
+## [0.4.0](https://github.com/guzzle/guzzle-services/tree/0.4.0) (2014-11-03)
+
+[Full Changelog](https://github.com/guzzle/guzzle-services/compare/0.3.0...0.4.0)
+
+**Closed issues:**
+
+- Exceptions Thrown From Subscribers Are Ignored? [\#58](https://github.com/guzzle/guzzle-services/issues/58)
+
+- Totally Broken With Guzzle 5 [\#57](https://github.com/guzzle/guzzle-services/issues/57)
+
+- GuzzleHTTP/Command Dependency fail [\#50](https://github.com/guzzle/guzzle-services/issues/50)
+
+- Request parameter PathLocation [\#46](https://github.com/guzzle/guzzle-services/issues/46)
+
+- Requesting a new version tag [\#45](https://github.com/guzzle/guzzle-services/issues/45)
+
+- CommandException expects second parameter to be CommandTransaction instance [\#43](https://github.com/guzzle/guzzle-services/issues/43)
+
+- Cannot add Autorization header to my requests [\#39](https://github.com/guzzle/guzzle-services/issues/39)
+
+- Resouce Itterators [\#36](https://github.com/guzzle/guzzle-services/issues/36)
+
+- Question [\#33](https://github.com/guzzle/guzzle-services/issues/33)
+
+- query location array can be comma separated [\#31](https://github.com/guzzle/guzzle-services/issues/31)
+
+- Automatically returns array from command? [\#30](https://github.com/guzzle/guzzle-services/issues/30)
+
+- Arrays nested under objects in JSON response broken? [\#27](https://github.com/guzzle/guzzle-services/issues/27)
+
+- Question? [\#23](https://github.com/guzzle/guzzle-services/issues/23)
+
+**Merged pull requests:**
+
+- Bump the version in the readme [\#60](https://github.com/guzzle/guzzle-services/pull/60) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Bump the next version to 0.4 [\#56](https://github.com/guzzle/guzzle-services/pull/56) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Fixed the guzzlehttp/command version constraint [\#55](https://github.com/guzzle/guzzle-services/pull/55) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Work with latest Guzzle 5 and Command updates [\#54](https://github.com/guzzle/guzzle-services/pull/54) ([mtdowling](https://github.com/mtdowling))
+
+- Addressing Issue \#51 & Issue \#52 [\#53](https://github.com/guzzle/guzzle-services/pull/53) ([sprak3000](https://github.com/sprak3000))
+
+- added description interface to extend it [\#49](https://github.com/guzzle/guzzle-services/pull/49) ([danieledangeli](https://github.com/danieledangeli))
+
+- Update readme to improve documentation \(\#46\) [\#48](https://github.com/guzzle/guzzle-services/pull/48) ([bonndan](https://github.com/bonndan))
+
+- Fixed the readme version constraint [\#42](https://github.com/guzzle/guzzle-services/pull/42) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Update .travis.yml [\#41](https://github.com/guzzle/guzzle-services/pull/41) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Added a branch alias [\#40](https://github.com/guzzle/guzzle-services/pull/40) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Fixes Response\XmlLocation requires text value [\#38](https://github.com/guzzle/guzzle-services/pull/38) ([magnetik](https://github.com/magnetik))
+
+- Removing unnecessary \(\) from docblock [\#32](https://github.com/guzzle/guzzle-services/pull/32) ([jamiehannaford](https://github.com/jamiehannaford))
+
+- Fix JSON response location so that both is supported: arrays nested unde... [\#28](https://github.com/guzzle/guzzle-services/pull/28) ([ukautz](https://github.com/ukautz))
+
+- Throw Any Exceptions On Process [\#59](https://github.com/guzzle/guzzle-services/pull/59) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Allow extension to work recursively over models [\#34](https://github.com/guzzle/guzzle-services/pull/34) ([jamiehannaford](https://github.com/jamiehannaford))
+
+- A custom class can be configured for command instances. [\#29](https://github.com/guzzle/guzzle-services/pull/29) ([robinvdvleuten](https://github.com/robinvdvleuten))
+
+- \[WIP\] doing some experimentation [\#24](https://github.com/guzzle/guzzle-services/pull/24) ([cordoval](https://github.com/cordoval))
+
+## [0.3.0](https://github.com/guzzle/guzzle-services/tree/0.3.0) (2014-06-01)
+
+[Full Changelog](https://github.com/guzzle/guzzle-services/compare/0.2.0...0.3.0)
+
+**Closed issues:**
+
+- Testing Guzzle Services doesn't work [\#19](https://github.com/guzzle/guzzle-services/issues/19)
+
+- Description factory [\#18](https://github.com/guzzle/guzzle-services/issues/18)
+
+- support to load service description from file [\#15](https://github.com/guzzle/guzzle-services/issues/15)
+
+- Update dependency on guzzlehttp/command [\#11](https://github.com/guzzle/guzzle-services/issues/11)
+
+**Merged pull requests:**
+
+- Add license file [\#22](https://github.com/guzzle/guzzle-services/pull/22) ([siwinski](https://github.com/siwinski))
+
+- Fix 'Invalid argument supplied for foreach\(\)' [\#21](https://github.com/guzzle/guzzle-services/pull/21) ([Olden](https://github.com/Olden))
+
+- Fixed string zero \('0'\) values not being filtered in XML. [\#20](https://github.com/guzzle/guzzle-services/pull/20) ([dragonwize](https://github.com/dragonwize))
+
+- baseUrl can be a string or an uri template [\#16](https://github.com/guzzle/guzzle-services/pull/16) ([robinvdvleuten](https://github.com/robinvdvleuten))
+
+## [0.2.0](https://github.com/guzzle/guzzle-services/tree/0.2.0) (2014-03-30)
+
+[Full Changelog](https://github.com/guzzle/guzzle-services/compare/0.1.0...0.2.0)
+
+**Closed issues:**
+
+- please remove wiki [\#13](https://github.com/guzzle/guzzle-services/issues/13)
+
+- Parameter validation fails for union types [\#12](https://github.com/guzzle/guzzle-services/issues/12)
+
+- question on integration with Guzzle4 [\#8](https://github.com/guzzle/guzzle-services/issues/8)
+
+- typehints for operations property [\#6](https://github.com/guzzle/guzzle-services/issues/6)
+
+- improve exception message [\#5](https://github.com/guzzle/guzzle-services/issues/5)
+
+**Merged pull requests:**
+
+- Update composer.json [\#14](https://github.com/guzzle/guzzle-services/pull/14) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Update composer.json [\#9](https://github.com/guzzle/guzzle-services/pull/9) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- some fixes [\#4](https://github.com/guzzle/guzzle-services/pull/4) ([cordoval](https://github.com/cordoval))
+
+- Fix the CommandException path used in ValidateInput [\#2](https://github.com/guzzle/guzzle-services/pull/2) ([mookle](https://github.com/mookle))
+
+- Minor improvements [\#1](https://github.com/guzzle/guzzle-services/pull/1) ([GrahamCampbell](https://github.com/GrahamCampbell))
+
+- Use latest guzzlehttp/command to fix dependencies [\#10](https://github.com/guzzle/guzzle-services/pull/10) ([sbward](https://github.com/sbward))
+
+- some collaboration using Gush :\) [\#3](https://github.com/guzzle/guzzle-services/pull/3) ([cordoval](https://github.com/cordoval))
+
+## [0.1.0](https://github.com/guzzle/guzzle-services/tree/0.1.0) (2014-03-15)
+
+
+
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/LICENSE b/server/addons/cos/library/Guzzle/guzzle-services/LICENSE
new file mode 100644
index 0000000..5d19603
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Michael Dowling
+Copyright (c) 2014 Graham Campbell
+Copyright (c) 2016 Stefano Kowalke
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/Makefile b/server/addons/cos/library/Guzzle/guzzle-services/Makefile
new file mode 100644
index 0000000..762a2ac
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/Makefile
@@ -0,0 +1,2 @@
+test:
+ vendor/bin/phpunit $(TEST)
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/README.md b/server/addons/cos/library/Guzzle/guzzle-services/README.md
new file mode 100644
index 0000000..d1b5ac5
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/README.md
@@ -0,0 +1,134 @@
+# Guzzle Services
+
+Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures.
+
+```php
+use GuzzleHttp\Client;
+use GuzzleHttp\Command\Guzzle\GuzzleClient;
+use GuzzleHttp\Command\Guzzle\Description;
+
+$client = new Client();
+$description = new Description([
+ 'baseUri' => 'http://httpbin.org/',
+ 'operations' => [
+ 'testing' => [
+ 'httpMethod' => 'GET',
+ 'uri' => '/get{?foo}',
+ 'responseModel' => 'getResponse',
+ 'parameters' => [
+ 'foo' => [
+ 'type' => 'string',
+ 'location' => 'uri'
+ ],
+ 'bar' => [
+ 'type' => 'string',
+ 'location' => 'query'
+ ]
+ ]
+ ]
+ ],
+ 'models' => [
+ 'getResponse' => [
+ 'type' => 'object',
+ 'additionalProperties' => [
+ 'location' => 'json'
+ ]
+ ]
+ ]
+]);
+
+$guzzleClient = new GuzzleClient($client, $description);
+
+$result = $guzzleClient->testing(['foo' => 'bar']);
+echo $result['args']['foo'];
+// bar
+```
+
+## Installing
+
+This project can be installed using Composer:
+
+``composer require guzzlehttp/guzzle-services``
+
+For **Guzzle 5**, use ``composer require guzzlehttp/guzzle-services:0.6``.
+
+**Note:** If Composer is not installed [globally](https://getcomposer.org/doc/00-intro.md#globally) then you may need to run the preceding Composer commands using ``php composer.phar`` (where ``composer.phar`` is the path to your copy of Composer), instead of just ``composer``.
+
+## Plugins
+
+* Load Service description from file [https://github.com/gimler/guzzle-description-loader]
+
+## Transition guide from Guzzle 5.0 to 6.0
+
+### Change regarding PostField and PostFile
+
+The request locations `postField` and `postFile` were removed in favor of `formParam` and `multipart`. If your description looks like
+
+```php
+[
+ 'baseUri' => 'http://httpbin.org/',
+ 'operations' => [
+ 'testing' => [
+ 'httpMethod' => 'GET',
+ 'uri' => '/get{?foo}',
+ 'responseModel' => 'getResponse',
+ 'parameters' => [
+ 'foo' => [
+ 'type' => 'string',
+ 'location' => 'postField'
+ ],
+ 'bar' => [
+ 'type' => 'string',
+ 'location' => 'postFile'
+ ]
+ ]
+ ]
+ ],
+]
+```
+
+you need to change `postField` to `formParam` and `postFile` to `multipart`.
+
+More documentation coming soon.
+
+## Cookbook
+
+### Changing the way query params are serialized
+
+By default, query params are serialized using strict RFC3986 rules, using `http_build_query` method. With this, array params are serialized this way:
+
+```php
+$client->myMethod(['foo' => ['bar', 'baz']]);
+
+// Query params will be foo[0]=bar&foo[1]=baz
+```
+
+However, a lot of APIs in the wild require the numeric indices to be removed, so that the query params end up being `foo[]=bar&foo[]=baz`. You
+can easily change the behaviour by creating your own serializer and overriding the "query" request location:
+
+```php
+use GuzzleHttp\Command\Guzzle\GuzzleClient;
+use GuzzleHttp\Command\Guzzle\RequestLocation\QueryLocation;
+use GuzzleHttp\Command\Guzzle\QuerySerializer\Rfc3986Serializer;
+use GuzzleHttp\Command\Guzzle\Serializer;
+
+$queryLocation = new QueryLocation('query', new Rfc3986Serializer(true));
+$serializer = new Serializer($description, ['query' => $queryLocation]);
+$guzzleClient = new GuzzleClient($client, $description, $serializer);
+```
+
+You can also create your own serializer if you have specific needs.
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/guzzle-services/security/policy) for more information.
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-guzzle-services?utm_source=packagist-guzzlehttp-guzzle-services&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/composer.json b/server/addons/cos/library/Guzzle/guzzle-services/composer.json
new file mode 100644
index 0000000..9fa76cc
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/composer.json
@@ -0,0 +1,59 @@
+{
+ "name": "guzzlehttp/guzzle-services",
+ "description": "Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures.",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Stefano Kowalke",
+ "email": "blueduck@mail.org",
+ "homepage": "https://github.com/Konafets"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "guzzlehttp/guzzle": "^7.4.1",
+ "guzzlehttp/command": "^1.2.2",
+ "guzzlehttp/psr7": "^1.8.3 || ^2.1",
+ "guzzlehttp/uri-template": "^1.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.19 || ^9.5.8"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Command\\Guzzle\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "GuzzleHttp\\Tests\\Command\\Guzzle\\": "tests/"
+ }
+ },
+ "suggest": {
+ "gimler/guzzle-description-loader": "^0.0.4"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "config": {
+ "preferred-install": "dist",
+ "sort-packages": true
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/phpunit.xml.dist b/server/addons/cos/library/Guzzle/guzzle-services/phpunit.xml.dist
new file mode 100644
index 0000000..140938d
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/phpunit.xml.dist
@@ -0,0 +1,23 @@
+
+
+
+
+ tests
+
+
+
+
+ src
+
+
+
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/Description.php b/server/addons/cos/library/Guzzle/guzzle-services/src/Description.php
new file mode 100644
index 0000000..b8d060e
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/Description.php
@@ -0,0 +1,265 @@
+{$key} = $config[$key];
+ }
+ }
+
+ // Set the baseUri
+ // Account for the old style of using baseUrl
+ if (isset($config['baseUrl'])) {
+ $config['baseUri'] = $config['baseUrl'];
+ }
+ $this->baseUri = isset($config['baseUri']) ? new Uri($config['baseUri']) : new Uri();
+
+ // Ensure that the models and operations properties are always arrays
+ $this->models = (array) $this->models;
+ $this->operations = (array) $this->operations;
+
+ // We want to add operations differently than adding the other properties
+ $defaultKeys[] = 'operations';
+
+ // Create operations for each operation
+ if (isset($config['operations'])) {
+ foreach ($config['operations'] as $name => $operation) {
+ if (!is_array($operation)) {
+ throw new \InvalidArgumentException('Operations must be arrays');
+ }
+ $this->operations[$name] = $operation;
+ }
+ }
+
+ // Get all of the additional properties of the service description and
+ // store them in a data array
+ foreach (array_diff(array_keys($config), $defaultKeys) as $key) {
+ $this->extraData[$key] = $config[$key];
+ }
+
+ // Configure the schema formatter
+ if (isset($options['formatter'])) {
+ $this->formatter = $options['formatter'];
+ } else {
+ static $defaultFormatter;
+ if (!$defaultFormatter) {
+ $defaultFormatter = new SchemaFormatter();
+ }
+ $this->formatter = $defaultFormatter;
+ }
+ }
+
+ /**
+ * Get the basePath/baseUri of the description
+ *
+ * @return Uri
+ */
+ public function getBaseUri()
+ {
+ return $this->baseUri;
+ }
+
+ /**
+ * Get the API operations of the service
+ *
+ * @return Operation[] Returns an array of {@see Operation} objects
+ */
+ public function getOperations()
+ {
+ return $this->operations;
+ }
+
+ /**
+ * Check if the service has an operation by name
+ *
+ * @param string $name Name of the operation to check
+ *
+ * @return bool
+ */
+ public function hasOperation($name)
+ {
+ return isset($this->operations[$name]);
+ }
+
+ /**
+ * Get an API operation by name
+ *
+ * @param string $name Name of the command
+ *
+ * @return Operation
+ * @throws \InvalidArgumentException if the operation is not found
+ */
+ public function getOperation($name)
+ {
+ if (!$this->hasOperation($name)) {
+ throw new \InvalidArgumentException("No operation found named $name");
+ }
+
+ // Lazily create operations as they are retrieved
+ if (!($this->operations[$name] instanceof Operation)) {
+ $this->operations[$name]['name'] = $name;
+ $this->operations[$name] = new Operation($this->operations[$name], $this);
+ }
+
+ return $this->operations[$name];
+ }
+
+ /**
+ * Get a shared definition structure.
+ *
+ * @param string $id ID/name of the model to retrieve
+ *
+ * @return Parameter
+ * @throws \InvalidArgumentException if the model is not found
+ */
+ public function getModel($id)
+ {
+ if (!$this->hasModel($id)) {
+ throw new \InvalidArgumentException("No model found named $id");
+ }
+
+ // Lazily create models as they are retrieved
+ if (!($this->models[$id] instanceof Parameter)) {
+ $this->models[$id] = new Parameter(
+ $this->models[$id],
+ ['description' => $this]
+ );
+ }
+
+ return $this->models[$id];
+ }
+
+ /**
+ * Get all models of the service description.
+ *
+ * @return array
+ */
+ public function getModels()
+ {
+ $models = [];
+ foreach ($this->models as $name => $model) {
+ $models[$name] = $this->getModel($name);
+ }
+
+ return $models;
+ }
+
+ /**
+ * Check if the service description has a model by name.
+ *
+ * @param string $id Name/ID of the model to check
+ *
+ * @return bool
+ */
+ public function hasModel($id)
+ {
+ return isset($this->models[$id]);
+ }
+
+ /**
+ * Get the API version of the service
+ *
+ * @return string
+ */
+ public function getApiVersion()
+ {
+ return $this->apiVersion;
+ }
+
+ /**
+ * Get the name of the API
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get a summary of the purpose of the API
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Format a parameter using named formats.
+ *
+ * @param string $format Format to convert it to
+ * @param mixed $input Input string
+ *
+ * @return mixed
+ */
+ public function format($format, $input)
+ {
+ return $this->formatter->format($format, $input);
+ }
+
+ /**
+ * Get arbitrary data from the service description that is not part of the
+ * Guzzle service description specification.
+ *
+ * @param string $key Data key to retrieve or null to retrieve all extra
+ *
+ * @return null|mixed
+ */
+ public function getData($key = null)
+ {
+ if ($key === null) {
+ return $this->extraData;
+ } elseif (isset($this->extraData[$key])) {
+ return $this->extraData[$key];
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/DescriptionInterface.php b/server/addons/cos/library/Guzzle/guzzle-services/src/DescriptionInterface.php
new file mode 100644
index 0000000..6b3adba
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/DescriptionInterface.php
@@ -0,0 +1,107 @@
+ new BodyLocation(),
+ 'header' => new HeaderLocation(),
+ 'reasonPhrase' => new ReasonPhraseLocation(),
+ 'statusCode' => new StatusCodeLocation(),
+ 'xml' => new XmlLocation(),
+ 'json' => new JsonLocation(),
+ ];
+ }
+
+ $this->responseLocations = $responseLocations + $defaultResponseLocations;
+ $this->description = $description;
+ $this->process = $process;
+ }
+
+ /**
+ * Deserialize the response into the specified result representation
+ *
+ * @param ResponseInterface $response
+ * @param RequestInterface|null $request
+ * @param CommandInterface $command
+ * @return Result|ResultInterface|void|ResponseInterface
+ */
+ public function __invoke(ResponseInterface $response, RequestInterface $request, CommandInterface $command)
+ {
+ // If the user don't want to process the result, just return the plain response here
+ if ($this->process === false) {
+ return $response;
+ }
+
+ $name = $command->getName();
+ $operation = $this->description->getOperation($name);
+
+ $this->handleErrorResponses($response, $request, $command, $operation);
+
+ // Add a default Model as the result if no matching schema was found
+ if (!($modelName = $operation->getResponseModel())) {
+ // Not sure if this should be empty or contains the response.
+ // Decided to do it how it was in the old version for now.
+ return new Result();
+ }
+
+ $model = $operation->getServiceDescription()->getModel($modelName);
+ if (!$model) {
+ throw new \RuntimeException("Unknown model: {$modelName}");
+ }
+
+ return $this->visit($model, $response);
+ }
+
+ /**
+ * Handles visit() and after() methods of the Response locations
+ *
+ * @param Parameter $model
+ * @param ResponseInterface $response
+ * @return Result|ResultInterface|void
+ */
+ protected function visit(Parameter $model, ResponseInterface $response)
+ {
+ $result = new Result();
+ $context = ['visitors' => []];
+
+ if ($model->getType() === 'object') {
+ $result = $this->visitOuterObject($model, $result, $response, $context);
+ } elseif ($model->getType() === 'array') {
+ $result = $this->visitOuterArray($model, $result, $response, $context);
+ } else {
+ throw new \InvalidArgumentException('Invalid response model: ' . $model->getType());
+ }
+
+ // Call the after() method of each found visitor
+ /** @var ResponseLocationInterface $visitor */
+ foreach ($context['visitors'] as $visitor) {
+ $result = $visitor->after($result, $response, $model);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Handles the before() method of Response locations
+ *
+ * @param string $location
+ * @param Parameter $model
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param array $context
+ * @return ResultInterface
+ */
+ private function triggerBeforeVisitor(
+ $location,
+ Parameter $model,
+ ResultInterface $result,
+ ResponseInterface $response,
+ array &$context
+ ) {
+ if (!isset($this->responseLocations[$location])) {
+ throw new \RuntimeException("Unknown location: $location");
+ }
+
+ $context['visitors'][$location] = $this->responseLocations[$location];
+
+ $result = $this->responseLocations[$location]->before(
+ $result,
+ $response,
+ $model
+ );
+
+ return $result;
+ }
+
+ /**
+ * Visits the outer object
+ *
+ * @param Parameter $model
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param array $context
+ * @return ResultInterface
+ */
+ private function visitOuterObject(
+ Parameter $model,
+ ResultInterface $result,
+ ResponseInterface $response,
+ array &$context
+ ) {
+ $parentLocation = $model->getLocation();
+
+ // If top-level additionalProperties is a schema, then visit it
+ $additional = $model->getAdditionalProperties();
+ if ($additional instanceof Parameter) {
+ // Use the model location if none set on additionalProperties.
+ $location = $additional->getLocation() ?: $parentLocation;
+ $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context);
+ }
+
+ // Use 'location' from all individual defined properties, but fall back
+ // to the model location if no per-property location is set. Collect
+ // the properties that need to be visited into an array.
+ $visitProperties = [];
+ foreach ($model->getProperties() as $schema) {
+ $location = $schema->getLocation() ?: $parentLocation;
+ if ($location) {
+ $visitProperties[] = [$location, $schema];
+ // Trigger the before method on each unique visitor location
+ if (!isset($context['visitors'][$location])) {
+ $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context);
+ }
+ }
+ }
+
+ // Actually visit each response element
+ foreach ($visitProperties as $property) {
+ $result = $this->responseLocations[$property[0]]->visit($result, $response, $property[1]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Visits the outer array
+ *
+ * @param Parameter $model
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param array $context
+ * @return ResultInterface|void
+ */
+ private function visitOuterArray(
+ Parameter $model,
+ ResultInterface $result,
+ ResponseInterface $response,
+ array &$context
+ ) {
+ // Use 'location' defined on the top of the model
+ if (!($location = $model->getLocation())) {
+ return;
+ }
+
+ // Trigger the before method on each unique visitor location
+ if (!isset($context['visitors'][$location])) {
+ $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context);
+ }
+
+ // Visit each item in the response
+ $result = $this->responseLocations[$location]->visit($result, $response, $model);
+
+ return $result;
+ }
+
+ /**
+ * Reads the "errorResponses" from commands, and trigger appropriate exceptions
+ *
+ * In order for the exception to be properly triggered, all your exceptions must be instance
+ * of "GuzzleHttp\Command\Exception\CommandException". If that's not the case, your exceptions will be wrapped
+ * around a CommandException
+ *
+ * @param ResponseInterface $response
+ * @param RequestInterface $request
+ * @param CommandInterface $command
+ * @param Operation $operation
+ */
+ protected function handleErrorResponses(
+ ResponseInterface $response,
+ RequestInterface $request,
+ CommandInterface $command,
+ Operation $operation
+ ) {
+ $errors = $operation->getErrorResponses();
+
+ // We iterate through each errors in service description. If the descriptor contains both a phrase and
+ // status code, there must be an exact match of both. Otherwise, a match of status code is enough
+ $bestException = null;
+
+ foreach ($errors as $error) {
+ $code = (int) $error['code'];
+
+ if ($response->getStatusCode() !== $code) {
+ continue;
+ }
+
+ if (isset($error['phrase']) && ! ($error['phrase'] === $response->getReasonPhrase())) {
+ continue;
+ }
+
+ $bestException = $error['class'];
+
+ // If there is an exact match of phrase + code, then we cannot find a more specialized exception in
+ // the array, so we can break early instead of iterating the remaining ones
+ if (isset($error['phrase'])) {
+ break;
+ }
+ }
+
+ if (null !== $bestException) {
+ throw new $bestException($response->getReasonPhrase(), $command, null, $request, $response);
+ }
+
+ // If we reach here, no exception could be match from descriptor, and Guzzle exception will propagate if
+ // option "http_errors" is set to true, which is the default setting.
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/GuzzleClient.php b/server/addons/cos/library/Guzzle/guzzle-services/src/GuzzleClient.php
new file mode 100644
index 0000000..fa656a3
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/GuzzleClient.php
@@ -0,0 +1,169 @@
+config = $config;
+ $this->description = $description;
+ $serializer = $this->getSerializer($commandToRequestTransformer);
+ $deserializer = $this->getDeserializer($responseToResultTransformer);
+
+ parent::__construct($client, $serializer, $deserializer, $commandHandlerStack);
+ $this->processConfig($config);
+ }
+
+ /**
+ * Returns the command if valid; otherwise an Exception
+ * @param string $name
+ * @param array $args
+ * @return CommandInterface
+ * @throws \InvalidArgumentException
+ */
+ public function getCommand($name, array $args = [])
+ {
+ if (!$this->description->hasOperation($name)) {
+ $name = ucfirst($name);
+ if (!$this->description->hasOperation($name)) {
+ throw new \InvalidArgumentException(
+ "No operation found named {$name}"
+ );
+ }
+ }
+
+ // Merge in default command options
+ $args += $this->getConfig('defaults');
+
+ return parent::getCommand($name, $args);
+ }
+
+ /**
+ * Return the description
+ *
+ * @return DescriptionInterface
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Returns the passed Serializer when set, a new instance otherwise
+ *
+ * @param callable|null $commandToRequestTransformer
+ * @return \GuzzleHttp\Command\Guzzle\Serializer
+ */
+ private function getSerializer($commandToRequestTransformer)
+ {
+ return $commandToRequestTransformer !== null
+ ? $commandToRequestTransformer
+ : new Serializer($this->description);
+ }
+
+ /**
+ * Returns the passed Deserializer when set, a new instance otherwise
+ *
+ * @param callable|null $responseToResultTransformer
+ * @return \GuzzleHttp\Command\Guzzle\Deserializer
+ */
+ private function getDeserializer($responseToResultTransformer)
+ {
+ $process = (! isset($this->config['process']) || $this->config['process'] === true);
+
+ return $responseToResultTransformer !== null
+ ? $responseToResultTransformer
+ : new Deserializer($this->description, $process);
+ }
+
+ /**
+ * Get the config of the client
+ *
+ * @param array|string $option
+ * @return mixed
+ */
+ public function getConfig($option = null)
+ {
+ return $option === null
+ ? $this->config
+ : (isset($this->config[$option]) ? $this->config[$option] : []);
+ }
+
+ /**
+ * @param $option
+ * @param $value
+ */
+ public function setConfig($option, $value)
+ {
+ $this->config[$option] = $value;
+ }
+
+ /**
+ * Prepares the client based on the configuration settings of the client.
+ *
+ * @param array $config Constructor config as an array
+ */
+ protected function processConfig(array $config)
+ {
+ // set defaults as an array if not provided
+ if (!isset($config['defaults'])) {
+ $config['defaults'] = [];
+ }
+
+ // Add the handlers based on the configuration option
+ $stack = $this->getHandlerStack();
+
+ if (!isset($config['validate']) || $config['validate'] === true) {
+ $stack->push(new ValidatedDescriptionHandler($this->description), 'validate_description');
+ }
+
+ if (!isset($config['process']) || $config['process'] === true) {
+ // TODO: This belongs to the Deserializer and should be handled there.
+ // Question: What is the result when the Deserializer is bypassed?
+ // Possible answer: The raw response.
+ }
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/Handler/ValidatedDescriptionHandler.php b/server/addons/cos/library/Guzzle/guzzle-services/src/Handler/ValidatedDescriptionHandler.php
new file mode 100644
index 0000000..c5ec2d6
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/Handler/ValidatedDescriptionHandler.php
@@ -0,0 +1,82 @@
+
+ */
+class ValidatedDescriptionHandler
+{
+ /** @var SchemaValidator $validator */
+ private $validator;
+
+ /** @var DescriptionInterface $description */
+ private $description;
+
+ /**
+ * ValidatedDescriptionHandler constructor.
+ *
+ * @param DescriptionInterface $description
+ * @param SchemaValidator|null $schemaValidator
+ */
+ public function __construct(DescriptionInterface $description, SchemaValidator $schemaValidator = null)
+ {
+ $this->description = $description;
+ $this->validator = $schemaValidator ?: new SchemaValidator();
+ }
+
+ /**
+ * @param callable $handler
+ * @return \Closure
+ */
+ public function __invoke(callable $handler)
+ {
+ return function (CommandInterface $command) use ($handler) {
+ $errors = [];
+ $operation = $this->description->getOperation($command->getName());
+
+ foreach ($operation->getParams() as $name => $schema) {
+ $value = $command[$name];
+
+ if ($value) {
+ $value = $schema->filter($value);
+ }
+
+ if (! $this->validator->validate($schema, $value)) {
+ $errors = array_merge($errors, $this->validator->getErrors());
+ } elseif ($value !== $command[$name]) {
+ // Update the config value if it changed and no validation errors were encountered.
+ // This happen when the user extending an operation
+ // See https://github.com/guzzle/guzzle-services/issues/145
+ $command[$name] = $value;
+ }
+ }
+
+ if ($params = $operation->getAdditionalParameters()) {
+ foreach ($command->toArray() as $name => $value) {
+ // It's only additional if it isn't defined in the schema
+ if (! $operation->hasParam($name)) {
+ // Always set the name so that error messages are useful
+ $params->setName($name);
+ if (! $this->validator->validate($params, $value)) {
+ $errors = array_merge($errors, $this->validator->getErrors());
+ } elseif ($value !== $command[$name]) {
+ $command[$name] = $value;
+ }
+ }
+ }
+ }
+
+ if ($errors) {
+ throw new CommandException('Validation errors: ' . implode("\n", $errors), $command);
+ }
+
+ return $handler($command);
+ };
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/Operation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/Operation.php
new file mode 100644
index 0000000..57b75ca
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/Operation.php
@@ -0,0 +1,312 @@
+ '',
+ 'httpMethod' => '',
+ 'uri' => '',
+ 'responseModel' => null,
+ 'notes' => '',
+ 'summary' => '',
+ 'documentationUrl' => null,
+ 'deprecated' => false,
+ 'data' => [],
+ 'parameters' => [],
+ 'additionalParameters' => null,
+ 'errorResponses' => []
+ ];
+
+ $this->description = $description === null ? new Description([]) : $description;
+
+ if (isset($config['extends'])) {
+ $config = $this->resolveExtends($config['extends'], $config);
+ }
+
+ $this->config = $config + $defaults;
+
+ // Account for the old style of using responseClass
+ if (isset($config['responseClass'])) {
+ $this->config['responseModel'] = $config['responseClass'];
+ }
+
+ $this->resolveParameters();
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Get the service description that the operation belongs to
+ *
+ * @return Description
+ */
+ public function getServiceDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Get the params of the operation
+ *
+ * @return Parameter[]
+ */
+ public function getParams()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Get additionalParameters of the operation
+ *
+ * @return Parameter|null
+ */
+ public function getAdditionalParameters()
+ {
+ return $this->additionalParameters;
+ }
+
+ /**
+ * Check if the operation has a specific parameter by name
+ *
+ * @param string $name Name of the param
+ *
+ * @return bool
+ */
+ public function hasParam($name)
+ {
+ return isset($this->parameters[$name]);
+ }
+
+ /**
+ * Get a single parameter of the operation
+ *
+ * @param string $name Parameter to retrieve by name
+ *
+ * @return Parameter|null
+ */
+ public function getParam($name)
+ {
+ return isset($this->parameters[$name])
+ ? $this->parameters[$name]
+ : null;
+ }
+
+ /**
+ * Get the HTTP method of the operation
+ *
+ * @return string|null
+ */
+ public function getHttpMethod()
+ {
+ return $this->config['httpMethod'];
+ }
+
+ /**
+ * Get the name of the operation
+ *
+ * @return string|null
+ */
+ public function getName()
+ {
+ return $this->config['name'];
+ }
+
+ /**
+ * Get a short summary of what the operation does
+ *
+ * @return string|null
+ */
+ public function getSummary()
+ {
+ return $this->config['summary'];
+ }
+
+ /**
+ * Get a longer text field to explain the behavior of the operation
+ *
+ * @return string|null
+ */
+ public function getNotes()
+ {
+ return $this->config['notes'];
+ }
+
+ /**
+ * Get the documentation URL of the operation
+ *
+ * @return string|null
+ */
+ public function getDocumentationUrl()
+ {
+ return $this->config['documentationUrl'];
+ }
+
+ /**
+ * Get the name of the model used for processing the response.
+ *
+ * @return string
+ */
+ public function getResponseModel()
+ {
+ return $this->config['responseModel'];
+ }
+
+ /**
+ * Get whether or not the operation is deprecated
+ *
+ * @return bool
+ */
+ public function getDeprecated()
+ {
+ return $this->config['deprecated'];
+ }
+
+ /**
+ * Get the URI that will be merged into the generated request
+ *
+ * @return string
+ */
+ public function getUri()
+ {
+ return $this->config['uri'];
+ }
+
+ /**
+ * Get the errors that could be encountered when executing the operation
+ *
+ * @return array
+ */
+ public function getErrorResponses()
+ {
+ return $this->config['errorResponses'];
+ }
+
+ /**
+ * Get extra data from the operation
+ *
+ * @param string $name Name of the data point to retrieve or null to
+ * retrieve all of the extra data.
+ *
+ * @return mixed|null
+ */
+ public function getData($name = null)
+ {
+ if ($name === null) {
+ return $this->config['data'];
+ } elseif (isset($this->config['data'][$name])) {
+ return $this->config['data'][$name];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param $name
+ * @param array $config
+ * @return array
+ */
+ private function resolveExtends($name, array $config)
+ {
+ if (!$this->description->hasOperation($name)) {
+ throw new \InvalidArgumentException('No operation named ' . $name);
+ }
+
+ // Merge parameters together one level deep
+ $base = $this->description->getOperation($name)->toArray();
+ $result = $config + $base;
+
+ if (isset($base['parameters']) && isset($config['parameters'])) {
+ $result['parameters'] = $config['parameters'] + $base['parameters'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Process the description and extract the parameter config
+ *
+ * @return void
+ */
+ private function resolveParameters()
+ {
+ // Parameters need special handling when adding
+ foreach ($this->config['parameters'] as $name => $param) {
+ if (!is_array($param)) {
+ throw new \InvalidArgumentException(
+ "Parameters must be arrays, {$this->config['name']}.$name is ".gettype($param)
+ );
+ }
+ $param['name'] = $name;
+ $this->parameters[$name] = new Parameter(
+ $param,
+ ['description' => $this->description]
+ );
+ }
+
+ if ($this->config['additionalParameters']) {
+ if (is_array($this->config['additionalParameters'])) {
+ $this->additionalParameters = new Parameter(
+ $this->config['additionalParameters'],
+ ['description' => $this->description]
+ );
+ } else {
+ $this->additionalParameters = $this->config['additionalParameters'];
+ }
+ }
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/Parameter.php b/server/addons/cos/library/Guzzle/guzzle-services/src/Parameter.php
new file mode 100644
index 0000000..8b3c39f
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/Parameter.php
@@ -0,0 +1,655 @@
+originalData = $data;
+
+ if (isset($options['description'])) {
+ $this->serviceDescription = $options['description'];
+ if (!($this->serviceDescription instanceof DescriptionInterface)) {
+ throw new \InvalidArgumentException('description must be a Description');
+ }
+ if (isset($data['$ref'])) {
+ if ($model = $this->serviceDescription->getModel($data['$ref'])) {
+ $name = isset($data['name']) ? $data['name'] : null;
+ $data = $model->toArray() + $data;
+ if ($name) {
+ $data['name'] = $name;
+ }
+ }
+ } elseif (isset($data['extends'])) {
+ // If this parameter extends from another parameter then start
+ // with the actual data union in the parent's data (e.g. actual
+ // supersedes parent)
+ if ($extends = $this->serviceDescription->getModel($data['extends'])) {
+ $data += $extends->toArray();
+ }
+ }
+ }
+
+ // Pull configuration data into the parameter
+ foreach ($data as $key => $value) {
+ $this->{$key} = $value;
+ }
+
+ $this->required = (bool) $this->required;
+ $this->data = (array) $this->data;
+
+ if ($this->filters) {
+ $this->setFilters((array) $this->filters);
+ }
+
+ if ($this->type == 'object' && $this->additionalProperties === null) {
+ $this->additionalProperties = true;
+ }
+ }
+
+ /**
+ * Convert the object to an array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->originalData;
+ }
+
+ /**
+ * Get the default or static value of the command based on a value
+ *
+ * @param string $value Value that is currently set
+ *
+ * @return mixed Returns the value, a static value if one is present, or a default value
+ */
+ public function getValue($value)
+ {
+ if ($this->static || ($this->default !== null && $value === null)) {
+ return $this->default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Run a value through the filters OR format attribute associated with the
+ * parameter.
+ *
+ * @param mixed $value Value to filter
+ *
+ * @return mixed Returns the filtered value
+ * @throws \RuntimeException when trying to format when no service
+ * description is available.
+ */
+ public function filter($value)
+ {
+ // Formats are applied exclusively and supersed filters
+ if ($this->format) {
+ if (!$this->serviceDescription) {
+ throw new \RuntimeException('No service description was set so '
+ . 'the value cannot be formatted.');
+ }
+ return $this->serviceDescription->format($this->format, $value);
+ }
+
+ // Convert Boolean values
+ if ($this->type == 'boolean' && !is_bool($value)) {
+ $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
+ }
+
+ // Apply filters to the value
+ if ($this->filters) {
+ foreach ($this->filters as $filter) {
+ if (is_array($filter)) {
+ // Convert complex filters that hold value place holders
+ foreach ($filter['args'] as &$data) {
+ if ($data == '@value') {
+ $data = $value;
+ } elseif ($data == '@api') {
+ $data = $this;
+ }
+ }
+ $value = call_user_func_array(
+ $filter['method'],
+ $filter['args']
+ );
+ } else {
+ $value = call_user_func($filter, $value);
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get the name of the parameter
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the parameter
+ *
+ * @param string $name Name to set
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Get the key of the parameter, where sentAs will supersede name if it is
+ * set.
+ *
+ * @return string
+ */
+ public function getWireName()
+ {
+ return $this->sentAs ?: $this->name;
+ }
+
+ /**
+ * Get the type(s) of the parameter
+ *
+ * @return string|array
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Get if the parameter is required
+ *
+ * @return bool
+ */
+ public function isRequired()
+ {
+ return $this->required;
+ }
+
+ /**
+ * Get the default value of the parameter
+ *
+ * @return string|null
+ */
+ public function getDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * Get the description of the parameter
+ *
+ * @return string|null
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Get the minimum acceptable value for an integer
+ *
+ * @return int|null
+ */
+ public function getMinimum()
+ {
+ return $this->minimum;
+ }
+
+ /**
+ * Get the maximum acceptable value for an integer
+ *
+ * @return int|null
+ */
+ public function getMaximum()
+ {
+ return $this->maximum;
+ }
+
+ /**
+ * Get the minimum allowed length of a string value
+ *
+ * @return int
+ */
+ public function getMinLength()
+ {
+ return $this->minLength;
+ }
+
+ /**
+ * Get the maximum allowed length of a string value
+ *
+ * @return int|null
+ */
+ public function getMaxLength()
+ {
+ return $this->maxLength;
+ }
+
+ /**
+ * Get the maximum allowed number of items in an array value
+ *
+ * @return int|null
+ */
+ public function getMaxItems()
+ {
+ return $this->maxItems;
+ }
+
+ /**
+ * Get the minimum allowed number of items in an array value
+ *
+ * @return int
+ */
+ public function getMinItems()
+ {
+ return $this->minItems;
+ }
+
+ /**
+ * Get the location of the parameter
+ *
+ * @return string|null
+ */
+ public function getLocation()
+ {
+ return $this->location;
+ }
+
+ /**
+ * Get the sentAs attribute of the parameter that used with locations to
+ * sentAs an attribute when it is being applied to a location.
+ *
+ * @return string|null
+ */
+ public function getSentAs()
+ {
+ return $this->sentAs;
+ }
+
+ /**
+ * Retrieve a known property from the parameter by name or a data property
+ * by name. When no specific name value is passed, all data properties
+ * will be returned.
+ *
+ * @param string|null $name Specify a particular property name to retrieve
+ *
+ * @return array|mixed|null
+ */
+ public function getData($name = null)
+ {
+ if (!$name) {
+ return $this->data;
+ } elseif (isset($this->data[$name])) {
+ return $this->data[$name];
+ } elseif (isset($this->{$name})) {
+ return $this->{$name};
+ }
+
+ return null;
+ }
+
+ /**
+ * Get whether or not the default value can be changed
+ *
+ * @return bool
+ */
+ public function isStatic()
+ {
+ return $this->static;
+ }
+
+ /**
+ * Get an array of filters used by the parameter
+ *
+ * @return array
+ */
+ public function getFilters()
+ {
+ return $this->filters ?: [];
+ }
+
+ /**
+ * Get the properties of the parameter
+ *
+ * @return Parameter[]
+ */
+ public function getProperties()
+ {
+ if (!$this->propertiesCache) {
+ $this->propertiesCache = [];
+ foreach (array_keys($this->properties) as $name) {
+ $this->propertiesCache[$name] = $this->getProperty($name);
+ }
+ }
+
+ return $this->propertiesCache;
+ }
+
+ /**
+ * Get a specific property from the parameter
+ *
+ * @param string $name Name of the property to retrieve
+ *
+ * @return null|Parameter
+ */
+ public function getProperty($name)
+ {
+ if (!isset($this->properties[$name])) {
+ return null;
+ }
+
+ if (!($this->properties[$name] instanceof self)) {
+ $this->properties[$name]['name'] = $name;
+ $this->properties[$name] = new static(
+ $this->properties[$name],
+ ['description' => $this->serviceDescription]
+ );
+ }
+
+ return $this->properties[$name];
+ }
+
+ /**
+ * Get the additionalProperties value of the parameter
+ *
+ * @return bool|Parameter|null
+ */
+ public function getAdditionalProperties()
+ {
+ if (is_array($this->additionalProperties)) {
+ $this->additionalProperties = new static(
+ $this->additionalProperties,
+ ['description' => $this->serviceDescription]
+ );
+ }
+
+ return $this->additionalProperties;
+ }
+
+ /**
+ * Get the item data of the parameter
+ *
+ * @return Parameter
+ */
+ public function getItems()
+ {
+ if (is_array($this->items)) {
+ $this->items = new static(
+ $this->items,
+ ['description' => $this->serviceDescription]
+ );
+ }
+
+ return $this->items;
+ }
+
+ /**
+ * Get the enum of strings that are valid for the parameter
+ *
+ * @return array|null
+ */
+ public function getEnum()
+ {
+ return $this->enum;
+ }
+
+ /**
+ * Get the regex pattern that must match a value when the value is a string
+ *
+ * @return string
+ */
+ public function getPattern()
+ {
+ return $this->pattern;
+ }
+
+ /**
+ * Get the format attribute of the schema
+ *
+ * @return string
+ */
+ public function getFormat()
+ {
+ return $this->format;
+ }
+
+ /**
+ * Set the array of filters used by the parameter
+ *
+ * @param array $filters Array of functions to use as filters
+ *
+ * @return self
+ */
+ private function setFilters(array $filters)
+ {
+ $this->filters = [];
+ foreach ($filters as $filter) {
+ $this->addFilter($filter);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a filter to the parameter
+ *
+ * @param string|array $filter Method to filter the value through
+ *
+ * @return self
+ * @throws \InvalidArgumentException
+ */
+ private function addFilter($filter)
+ {
+ if (is_array($filter)) {
+ if (!isset($filter['method'])) {
+ throw new \InvalidArgumentException(
+ 'A [method] value must be specified for each complex filter'
+ );
+ }
+ }
+
+ if (!$this->filters) {
+ $this->filters = [$filter];
+ } else {
+ $this->filters[] = $filter;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Check if a parameter has a specific variable and if it set.
+ *
+ * @param string $var
+ * @return bool
+ */
+ public function has($var)
+ {
+ if (!is_string($var)) {
+ throw new \InvalidArgumentException('Expected a string. Got: ' . (is_object($var) ? get_class($var) : gettype($var)));
+ }
+ return isset($this->{$var}) && !empty($this->{$var});
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/QuerySerializer/QuerySerializerInterface.php b/server/addons/cos/library/Guzzle/guzzle-services/src/QuerySerializer/QuerySerializerInterface.php
new file mode 100644
index 0000000..ad7fb11
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/QuerySerializer/QuerySerializerInterface.php
@@ -0,0 +1,13 @@
+removeNumericIndices = $removeNumericIndices;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function aggregate(array $queryParams)
+ {
+ $queryString = http_build_query($queryParams, '', '&', PHP_QUERY_RFC3986);
+
+ if ($this->removeNumericIndices) {
+ $queryString = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $queryString);
+ }
+
+ return $queryString;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/AbstractLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/AbstractLocation.php
new file mode 100644
index 0000000..29b484b
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/AbstractLocation.php
@@ -0,0 +1,101 @@
+locationName = $locationName;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Parameter $param
+ * @return RequestInterface
+ */
+ public function visit(
+ CommandInterface $command,
+ RequestInterface $request,
+ Parameter $param
+ ) {
+ return $request;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ return $request;
+ }
+
+ /**
+ * Prepare (filter and set desired name for request item) the value for
+ * request.
+ *
+ * @param mixed $value
+ * @param Parameter $param
+ *
+ * @return array|mixed
+ */
+ protected function prepareValue($value, Parameter $param)
+ {
+ return is_array($value)
+ ? $this->resolveRecursively($value, $param)
+ : $param->filter($value);
+ }
+
+ /**
+ * Recursively prepare and filter nested values.
+ *
+ * @param array $value Value to map
+ * @param Parameter $param Parameter related to the current key.
+ *
+ * @return array Returns the mapped array
+ */
+ protected function resolveRecursively(array $value, Parameter $param)
+ {
+ foreach ($value as $name => &$v) {
+ switch ($param->getType()) {
+ case 'object':
+ if ($subParam = $param->getProperty($name)) {
+ $key = $subParam->getWireName();
+ $value[$key] = $this->prepareValue($v, $subParam);
+ if ($name != $key) {
+ unset($value[$name]);
+ }
+ } elseif ($param->getAdditionalProperties() instanceof Parameter) {
+ $v = $this->prepareValue($v, $param->getAdditionalProperties());
+ }
+ break;
+ case 'array':
+ if ($items = $param->getItems()) {
+ $v = $this->prepareValue($v, $items);
+ }
+ break;
+ }
+ }
+
+ return $param->filter($value);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/BodyLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/BodyLocation.php
new file mode 100644
index 0000000..07f7203
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/BodyLocation.php
@@ -0,0 +1,49 @@
+getBody()->getContents();
+
+ $value = $command[$param->getName()];
+ $value = $param->getName() . '=' . $param->filter($value);
+
+ if ($oldValue !== '') {
+ $value = $oldValue . '&' . $value;
+ }
+
+ return $request->withBody(Psr7\Utils::streamFor($value));
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/FormParamLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/FormParamLocation.php
new file mode 100644
index 0000000..1a52df8
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/FormParamLocation.php
@@ -0,0 +1,83 @@
+formParamsData['form_params'][$param->getWireName()] = $this->prepareValue(
+ $command[$param->getName()],
+ $param
+ );
+
+ return $request;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ *
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ $data = $this->formParamsData;
+ $this->formParamsData = [];
+ $modify = [];
+
+ // Add additional parameters to the form_params array
+ $additional = $operation->getAdditionalParameters();
+ if ($additional && $additional->getLocation() == $this->locationName) {
+ foreach ($command->toArray() as $key => $value) {
+ if (!$operation->hasParam($key)) {
+ $data['form_params'][$key] = $this->prepareValue($value, $additional);
+ }
+ }
+ }
+
+ $body = http_build_query($data['form_params'], '', '&');
+ $modify['body'] = Psr7\Utils::streamFor($body);
+ $modify['set_headers']['Content-Type'] = $this->contentType;
+
+ return Psr7\Utils::modifyRequest($request, $modify);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/HeaderLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/HeaderLocation.php
new file mode 100644
index 0000000..cb067c4
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/HeaderLocation.php
@@ -0,0 +1,67 @@
+getName()];
+
+ return $request->withHeader($param->getWireName(), $param->filter($value));
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ *
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ /** @var Parameter $additional */
+ $additional = $operation->getAdditionalParameters();
+ if ($additional && ($additional->getLocation() === $this->locationName)) {
+ foreach ($command->toArray() as $key => $value) {
+ if (!$operation->hasParam($key)) {
+ $request = $request->withHeader($key, $additional->filter($value));
+ }
+ }
+ }
+
+ return $request;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/JsonLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/JsonLocation.php
new file mode 100644
index 0000000..71c4966
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/JsonLocation.php
@@ -0,0 +1,86 @@
+jsonContentType = $contentType;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Parameter $param
+ *
+ * @return RequestInterface
+ */
+ public function visit(
+ CommandInterface $command,
+ RequestInterface $request,
+ Parameter $param
+ ) {
+ $this->jsonData[$param->getWireName()] = $this->prepareValue(
+ $command[$param->getName()],
+ $param
+ );
+
+ return $request->withBody(Psr7\Utils::streamFor(Utils::jsonEncode($this->jsonData)));
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ *
+ * @return MessageInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ $data = $this->jsonData;
+ $this->jsonData = [];
+
+ // Add additional parameters to the JSON document
+ $additional = $operation->getAdditionalParameters();
+ if ($additional && ($additional->getLocation() === $this->locationName)) {
+ foreach ($command->toArray() as $key => $value) {
+ if (!$operation->hasParam($key)) {
+ $data[$key] = $this->prepareValue($value, $additional);
+ }
+ }
+ }
+
+ // Don't overwrite the Content-Type if one is set
+ if ($this->jsonContentType && !$request->hasHeader('Content-Type')) {
+ $request = $request->withHeader('Content-Type', $this->jsonContentType);
+ }
+
+ return $request->withBody(Psr7\Utils::streamFor(Utils::jsonEncode($data)));
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/MultiPartLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/MultiPartLocation.php
new file mode 100644
index 0000000..1123d3e
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/MultiPartLocation.php
@@ -0,0 +1,76 @@
+multipartData[] = [
+ 'name' => $param->getWireName(),
+ 'contents' => $this->prepareValue($command[$param->getName()], $param)
+ ];
+
+ return $request;
+ }
+
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ $data = $this->multipartData;
+ $this->multipartData = [];
+ $modify = [];
+
+ $body = new Psr7\MultipartStream($data);
+ $modify['body'] = Psr7\Utils::streamFor($body);
+ $request = Psr7\Utils::modifyRequest($request, $modify);
+ if ($request->getBody() instanceof Psr7\MultipartStream) {
+ // Use a multipart/form-data POST if a Content-Type is not set.
+ $request->withHeader('Content-Type', $this->contentType . $request->getBody()->getBoundary());
+ }
+
+ return $request;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/QueryLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/QueryLocation.php
new file mode 100644
index 0000000..c55643c
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/QueryLocation.php
@@ -0,0 +1,92 @@
+querySerializer = $querySerializer ?: new Rfc3986Serializer();
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Parameter $param
+ *
+ * @return RequestInterface
+ */
+ public function visit(
+ CommandInterface $command,
+ RequestInterface $request,
+ Parameter $param
+ ) {
+ $uri = $request->getUri();
+ $query = Psr7\Query::parse($uri->getQuery());
+
+ $query[$param->getWireName()] = $this->prepareValue(
+ $command[$param->getName()],
+ $param
+ );
+
+ $uri = $uri->withQuery($this->querySerializer->aggregate($query));
+
+ return $request->withUri($uri);
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ *
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ $additional = $operation->getAdditionalParameters();
+ if ($additional && $additional->getLocation() == $this->locationName) {
+ foreach ($command->toArray() as $key => $value) {
+ if (!$operation->hasParam($key)) {
+ $uri = $request->getUri();
+ $query = Psr7\Query::parse($uri->getQuery());
+
+ $query[$key] = $this->prepareValue(
+ $value,
+ $additional
+ );
+
+ $uri = $uri->withQuery($this->querySerializer->aggregate($query));
+ $request = $request->withUri($uri);
+ }
+ }
+ }
+
+ return $request;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/RequestLocationInterface.php b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/RequestLocationInterface.php
new file mode 100644
index 0000000..c0350ff
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/RequestLocation/RequestLocationInterface.php
@@ -0,0 +1,44 @@
+contentType = $contentType;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Parameter $param
+ *
+ * @return RequestInterface
+ */
+ public function visit(
+ CommandInterface $command,
+ RequestInterface $request,
+ Parameter $param
+ ) {
+ // Buffer and order the parameters to visit based on if they are
+ // top-level attributes or child nodes.
+ // @link https://github.com/guzzle/guzzle/pull/494
+ if ($param->getData('xmlAttribute')) {
+ array_unshift($this->buffered, $param);
+ } else {
+ $this->buffered[] = $param;
+ }
+
+ return $request;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @param RequestInterface $request
+ * @param Operation $operation
+ *
+ * @return RequestInterface
+ */
+ public function after(
+ CommandInterface $command,
+ RequestInterface $request,
+ Operation $operation
+ ) {
+ foreach ($this->buffered as $param) {
+ $this->visitWithValue(
+ $command[$param->getName()],
+ $param,
+ $operation
+ );
+ }
+
+ $this->buffered = [];
+
+ $additional = $operation->getAdditionalParameters();
+ if ($additional && $additional->getLocation() == $this->locationName) {
+ foreach ($command->toArray() as $key => $value) {
+ if (!$operation->hasParam($key)) {
+ $additional->setName($key);
+ $this->visitWithValue($value, $additional, $operation);
+ }
+ }
+ $additional->setName(null);
+ }
+
+ // If data was found that needs to be serialized, then do so
+ $xml = '';
+ if ($this->writer) {
+ $xml = $this->finishDocument($this->writer);
+ } elseif ($operation->getData('xmlAllowEmpty')) {
+ // Check if XML should always be sent for the command
+ $writer = $this->createRootElement($operation);
+ $xml = $this->finishDocument($writer);
+ }
+
+ if ($xml !== '') {
+ $request = $request->withBody(Psr7\Utils::streamFor($xml));
+ // Don't overwrite the Content-Type if one is set
+ if ($this->contentType && !$request->hasHeader('Content-Type')) {
+ $request = $request->withHeader('Content-Type', $this->contentType);
+ }
+ }
+
+ $this->writer = null;
+
+ return $request;
+ }
+
+ /**
+ * Create the root XML element to use with a request
+ *
+ * @param Operation $operation Operation object
+ *
+ * @return \XMLWriter
+ */
+ protected function createRootElement(Operation $operation)
+ {
+ static $defaultRoot = ['name' => 'Request'];
+ // If no root element was specified, then just wrap the XML in 'Request'
+ $root = $operation->getData('xmlRoot') ?: $defaultRoot;
+ // Allow the XML declaration to be customized with xmlEncoding
+ $encoding = $operation->getData('xmlEncoding');
+ $writer = $this->startDocument($encoding);
+ $writer->startElement($root['name']);
+
+ // Create the wrapping element with no namespaces if no namespaces were present
+ if (!empty($root['namespaces'])) {
+ // Create the wrapping element with an array of one or more namespaces
+ foreach ((array) $root['namespaces'] as $prefix => $uri) {
+ $nsLabel = 'xmlns';
+ if (!is_numeric($prefix)) {
+ $nsLabel .= ':'.$prefix;
+ }
+ $writer->writeAttribute($nsLabel, $uri);
+ }
+ }
+
+ return $writer;
+ }
+
+ /**
+ * Recursively build the XML body
+ *
+ * @param \XMLWriter $writer XML to modify
+ * @param Parameter $param API Parameter
+ * @param mixed $value Value to add
+ */
+ protected function addXml(\XMLWriter $writer, Parameter $param, $value)
+ {
+ $value = $param->filter($value);
+ $type = $param->getType();
+ $name = $param->getWireName();
+ $prefix = null;
+ $namespace = $param->getData('xmlNamespace');
+ if (false !== strpos($name, ':')) {
+ list($prefix, $name) = explode(':', $name, 2);
+ }
+
+ if ($type == 'object' || $type == 'array') {
+ if (!$param->getData('xmlFlattened')) {
+ if ($namespace) {
+ $writer->startElementNS(null, $name, $namespace);
+ } else {
+ $writer->startElement($name);
+ }
+ }
+ if ($param->getType() == 'array') {
+ $this->addXmlArray($writer, $param, $value);
+ } elseif ($param->getType() == 'object') {
+ $this->addXmlObject($writer, $param, $value);
+ }
+ if (!$param->getData('xmlFlattened')) {
+ $writer->endElement();
+ }
+ return;
+ }
+ if ($param->getData('xmlAttribute')) {
+ $this->writeAttribute($writer, $prefix, $name, $namespace, $value);
+ } else {
+ $this->writeElement($writer, $prefix, $name, $namespace, $value);
+ }
+ }
+
+ /**
+ * Write an attribute with namespace if used
+ *
+ * @param \XMLWriter $writer XMLWriter instance
+ * @param string $prefix Namespace prefix if any
+ * @param string $name Attribute name
+ * @param string $namespace The uri of the namespace
+ * @param string $value The attribute content
+ */
+ protected function writeAttribute($writer, $prefix, $name, $namespace, $value)
+ {
+ if ($namespace) {
+ $writer->writeAttributeNS($prefix, $name, $namespace, $value);
+ } else {
+ $writer->writeAttribute($name, $value);
+ }
+ }
+
+ /**
+ * Write an element with namespace if used
+ *
+ * @param \XMLWriter $writer XML writer resource
+ * @param string $prefix Namespace prefix if any
+ * @param string $name Element name
+ * @param string $namespace The uri of the namespace
+ * @param string $value The element content
+ */
+ protected function writeElement(\XMLWriter $writer, $prefix, $name, $namespace, $value)
+ {
+ if ($namespace) {
+ $writer->startElementNS($prefix, $name, $namespace);
+ } else {
+ $writer->startElement($name);
+ }
+ if (strpbrk($value, '<>&')) {
+ $writer->writeCData($value);
+ } else {
+ $writer->writeRaw($value);
+ }
+ $writer->endElement();
+ }
+
+ /**
+ * Create a new xml writer and start a document
+ *
+ * @param string $encoding document encoding
+ *
+ * @return \XMLWriter the writer resource
+ * @throws \RuntimeException if the document cannot be started
+ */
+ protected function startDocument($encoding)
+ {
+ $this->writer = new \XMLWriter();
+ if (!$this->writer->openMemory()) {
+ throw new \RuntimeException('Unable to open XML document in memory');
+ }
+ if (!$this->writer->startDocument('1.0', $encoding)) {
+ throw new \RuntimeException('Unable to start XML document');
+ }
+
+ return $this->writer;
+ }
+
+ /**
+ * End the document and return the output
+ *
+ * @param \XMLWriter $writer
+ *
+ * @return string the writer resource
+ */
+ protected function finishDocument($writer)
+ {
+ $writer->endDocument();
+
+ return $writer->outputMemory();
+ }
+
+ /**
+ * Add an array to the XML
+ *
+ * @param \XMLWriter $writer
+ * @param Parameter $param
+ * @param $value
+ */
+ protected function addXmlArray(\XMLWriter $writer, Parameter $param, &$value)
+ {
+ if ($items = $param->getItems()) {
+ foreach ($value as $v) {
+ $this->addXml($writer, $items, $v);
+ }
+ }
+ }
+
+ /**
+ * Add an object to the XML
+ *
+ * @param \XMLWriter $writer
+ * @param Parameter $param
+ * @param $value
+ */
+ protected function addXmlObject(\XMLWriter $writer, Parameter $param, &$value)
+ {
+ $noAttributes = [];
+
+ // add values which have attributes
+ foreach ($value as $name => $v) {
+ if ($property = $param->getProperty($name)) {
+ if ($property->getData('xmlAttribute')) {
+ $this->addXml($writer, $property, $v);
+ } else {
+ $noAttributes[] = ['value' => $v, 'property' => $property];
+ }
+ }
+ }
+
+ // now add values with no attributes
+ foreach ($noAttributes as $element) {
+ $this->addXml($writer, $element['property'], $element['value']);
+ }
+ }
+
+ /**
+ * @param $value
+ * @param Parameter $param
+ * @param Operation $operation
+ */
+ private function visitWithValue(
+ $value,
+ Parameter $param,
+ Operation $operation
+ ) {
+ if (!$this->writer) {
+ $this->createRootElement($operation);
+ }
+
+ $this->addXml($this->writer, $param, $value);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/AbstractLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/AbstractLocation.php
new file mode 100644
index 0000000..97adc72
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/AbstractLocation.php
@@ -0,0 +1,69 @@
+locationName = $locationName;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $model
+ * @return ResultInterface
+ */
+ public function before(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $model
+ ) {
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $model
+ * @return ResultInterface
+ */
+ public function after(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $model
+ ) {
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $param
+ * @return ResultInterface
+ */
+ public function visit(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $param
+ ) {
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/BodyLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/BodyLocation.php
new file mode 100644
index 0000000..f21d60a
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/BodyLocation.php
@@ -0,0 +1,39 @@
+getName()] = $param->filter($response->getBody());
+
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/HeaderLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/HeaderLocation.php
new file mode 100644
index 0000000..d156aff
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/HeaderLocation.php
@@ -0,0 +1,47 @@
+getName();
+ if ($header = $response->getHeader($param->getWireName())) {
+ if (is_array($header)) {
+ $header = array_shift($header);
+ }
+ $result[$name] = $param->filter($header);
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/JsonLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/JsonLocation.php
new file mode 100644
index 0000000..f94c784
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/JsonLocation.php
@@ -0,0 +1,176 @@
+getBody();
+ $body = $body ?: "{}";
+ $this->json = \GuzzleHttp\json_decode($body, true);
+ // relocate named arrays, so that they have the same structure as
+ // arrays nested in objects and visit can work on them in the same way
+ if ($model->getType() === 'array' && ($name = $model->getName())) {
+ $this->json = [$name => $this->json];
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $model
+ * @return ResultInterface
+ */
+ public function after(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $model
+ ) {
+ // Handle additional, undefined properties
+ $additional = $model->getAdditionalProperties();
+ if (!($additional instanceof Parameter)) {
+ return $result;
+ }
+
+ // Use the model location as the default if one is not set on additional
+ $addLocation = $additional->getLocation() ?: $model->getLocation();
+ if ($addLocation == $this->locationName) {
+ foreach ($this->json as $prop => $val) {
+ if (!isset($result[$prop])) {
+ // Only recurse if there is a type specified
+ $result[$prop] = $additional->getType()
+ ? $this->recurse($additional, $val)
+ : $val;
+ }
+ }
+ }
+
+ $this->json = [];
+
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $param
+ * @return Result|ResultInterface
+ */
+ public function visit(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $param
+ ) {
+ $name = $param->getName();
+ $key = $param->getWireName();
+
+ // Check if the result should be treated as a list
+ if ($param->getType() == 'array') {
+ // Treat as javascript array
+ if ($name) {
+ // name provided, store it under a key in the array
+ $subArray = isset($this->json[$key]) ? $this->json[$key] : null;
+ $result[$name] = $this->recurse($param, $subArray);
+ } else {
+ // top-level `array` or an empty name
+ $result = new Result(array_merge(
+ $result->toArray(),
+ $this->recurse($param, $this->json)
+ ));
+ }
+ } elseif (isset($this->json[$key])) {
+ $result[$name] = $this->recurse($param, $this->json[$key]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Recursively process a parameter while applying filters
+ *
+ * @param Parameter $param API parameter being validated
+ * @param mixed $value Value to process.
+ * @return mixed|null
+ */
+ private function recurse(Parameter $param, $value)
+ {
+ if (!is_array($value)) {
+ return $param->filter($value);
+ }
+
+ $result = [];
+ $type = $param->getType();
+
+ if ($type == 'array') {
+ $items = $param->getItems();
+ foreach ($value as $val) {
+ $result[] = $this->recurse($items, $val);
+ }
+ } elseif ($type == 'object' && !isset($value[0])) {
+ // On the above line, we ensure that the array is associative and
+ // not numerically indexed
+ if ($properties = $param->getProperties()) {
+ foreach ($properties as $property) {
+ $key = $property->getWireName();
+ if (array_key_exists($key, $value)) {
+ $result[$property->getName()] = $this->recurse(
+ $property,
+ $value[$key]
+ );
+ // Remove from the value so that AP can later be handled
+ unset($value[$key]);
+ }
+ }
+ }
+ // Only check additional properties if everything wasn't already
+ // handled
+ if ($value) {
+ $additional = $param->getAdditionalProperties();
+ if ($additional === null || $additional === true) {
+ // Merge the JSON under the resulting array
+ $result += $value;
+ } elseif ($additional instanceof Parameter) {
+ // Process all child elements according to the given schema
+ foreach ($value as $prop => $val) {
+ $result[$prop] = $this->recurse($additional, $val);
+ }
+ }
+ }
+ }
+
+ return $param->filter($result);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php
new file mode 100644
index 0000000..1cb590f
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php
@@ -0,0 +1,41 @@
+getName()] = $param->filter(
+ $response->getReasonPhrase()
+ );
+
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php
new file mode 100644
index 0000000..8669dff
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php
@@ -0,0 +1,61 @@
+getName()] = $param->filter($response->getStatusCode());
+
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/XmlLocation.php b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/XmlLocation.php
new file mode 100644
index 0000000..9450909
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/ResponseLocation/XmlLocation.php
@@ -0,0 +1,311 @@
+xml = simplexml_load_string((string) $response->getBody());
+
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $model
+ * @return Result|ResultInterface
+ */
+ public function after(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $model
+ ) {
+ // Handle additional, undefined properties
+ $additional = $model->getAdditionalProperties();
+ if ($additional instanceof Parameter &&
+ $additional->getLocation() == $this->locationName
+ ) {
+ $result = new Result(array_merge(
+ $result->toArray(),
+ self::xmlToArray($this->xml)
+ ));
+ }
+
+ $this->xml = null;
+
+ return $result;
+ }
+
+ /**
+ * @param ResultInterface $result
+ * @param ResponseInterface $response
+ * @param Parameter $param
+ * @return ResultInterface
+ */
+ public function visit(
+ ResultInterface $result,
+ ResponseInterface $response,
+ Parameter $param
+ ) {
+ $sentAs = $param->getWireName();
+ $ns = null;
+ if (strstr($sentAs, ':')) {
+ list($ns, $sentAs) = explode(':', $sentAs);
+ }
+
+ // Process the primary property
+ if (count($this->xml->children($ns, true)->{$sentAs})) {
+ $result[$param->getName()] = $this->recursiveProcess(
+ $param,
+ $this->xml->children($ns, true)->{$sentAs}
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Recursively process a parameter while applying filters
+ *
+ * @param Parameter $param API parameter being processed
+ * @param \SimpleXMLElement $node Node being processed
+ * @return array
+ */
+ private function recursiveProcess(
+ Parameter $param,
+ \SimpleXMLElement $node
+ ) {
+ $result = [];
+ $type = $param->getType();
+
+ if ($type == 'object') {
+ $result = $this->processObject($param, $node);
+ } elseif ($type == 'array') {
+ $result = $this->processArray($param, $node);
+ } else {
+ // We are probably handling a flat data node (i.e. string or
+ // integer), so let's check if it's childless, which indicates a
+ // node containing plain text.
+ if ($node->children()->count() == 0) {
+ // Retrieve text from node
+ $result = (string) $node;
+ }
+ }
+
+ // Filter out the value
+ if (isset($result)) {
+ $result = $param->filter($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Parameter $param
+ * @param \SimpleXMLElement $node
+ * @return array
+ */
+ private function processArray(Parameter $param, \SimpleXMLElement $node)
+ {
+ // Cast to an array if the value was a string, but should be an array
+ $items = $param->getItems();
+ $sentAs = $items->getWireName();
+ $result = [];
+ $ns = null;
+
+ if (strstr($sentAs, ':')) {
+ // Get namespace from the wire name
+ list($ns, $sentAs) = explode(':', $sentAs);
+ } else {
+ // Get namespace from data
+ $ns = $items->getData('xmlNs');
+ }
+
+ if ($sentAs === null) {
+ // A general collection of nodes
+ foreach ($node as $child) {
+ $result[] = $this->recursiveProcess($items, $child);
+ }
+ } else {
+ // A collection of named, repeating nodes
+ // (i.e. )
+ $children = $node->children($ns, true)->{$sentAs};
+ foreach ($children as $child) {
+ $result[] = $this->recursiveProcess($items, $child);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Process an object
+ *
+ * @param Parameter $param API parameter being parsed
+ * @param \SimpleXMLElement $node Value to process
+ * @return array
+ */
+ private function processObject(Parameter $param, \SimpleXMLElement $node)
+ {
+ $result = $knownProps = $knownAttributes = [];
+
+ // Handle known properties
+ if ($properties = $param->getProperties()) {
+ foreach ($properties as $property) {
+ $name = $property->getName();
+ $sentAs = $property->getWireName();
+ $knownProps[$sentAs] = 1;
+ if (strpos($sentAs, ':')) {
+ list($ns, $sentAs) = explode(':', $sentAs);
+ } else {
+ $ns = $property->getData('xmlNs');
+ }
+
+ if ($property->getData('xmlAttribute')) {
+ // Handle XML attributes
+ $result[$name] = (string) $node->attributes($ns, true)->{$sentAs};
+ $knownAttributes[$sentAs] = 1;
+ } elseif (count($node->children($ns, true)->{$sentAs})) {
+ // Found a child node matching wire name
+ $childNode = $node->children($ns, true)->{$sentAs};
+ $result[$name] = $this->recursiveProcess(
+ $property,
+ $childNode
+ );
+ }
+ }
+ }
+
+ // Handle additional, undefined properties
+ $additional = $param->getAdditionalProperties();
+ if ($additional instanceof Parameter) {
+ // Process all child elements according to the given schema
+ foreach ($node->children($additional->getData('xmlNs'), true) as $childNode) {
+ $sentAs = $childNode->getName();
+ if (!isset($knownProps[$sentAs])) {
+ $result[$sentAs] = $this->recursiveProcess(
+ $additional,
+ $childNode
+ );
+ }
+ }
+ } elseif ($additional === null || $additional === true) {
+ // Blindly transform the XML into an array preserving as much data
+ // as possible. Remove processed, aliased properties.
+ $array = array_diff_key(self::xmlToArray($node), $knownProps);
+ // Remove @attributes that were explicitly plucked from the
+ // attributes list.
+ if (isset($array['@attributes']) && $knownAttributes) {
+ $array['@attributes'] = array_diff_key($array['@attributes'], $knownProps);
+ if (!$array['@attributes']) {
+ unset($array['@attributes']);
+ }
+ }
+
+ // Merge it together with the original result
+ $result = array_merge($array, $result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert an XML document to an array.
+ *
+ * @param \SimpleXMLElement $xml
+ * @param int $nesting
+ * @param null $ns
+ *
+ * @return array
+ */
+ private static function xmlToArray(
+ \SimpleXMLElement $xml,
+ $ns = null,
+ $nesting = 0
+ ) {
+ $result = [];
+ $children = $xml->children($ns, true);
+
+ foreach ($children as $name => $child) {
+ $attributes = (array) $child->attributes($ns, true);
+ if (!isset($result[$name])) {
+ $childArray = self::xmlToArray($child, $ns, $nesting + 1);
+ $result[$name] = $attributes
+ ? array_merge($attributes, $childArray)
+ : $childArray;
+ continue;
+ }
+ // A child element with this name exists so we're assuming
+ // that the node contains a list of elements
+ if (!is_array($result[$name])) {
+ $result[$name] = [$result[$name]];
+ } elseif (!isset($result[$name][0])) {
+ // Convert the first child into the first element of a numerically indexed array
+ $firstResult = $result[$name];
+ $result[$name] = [];
+ $result[$name][] = $firstResult;
+ }
+ $childArray = self::xmlToArray($child, $ns, $nesting + 1);
+ if ($attributes) {
+ $result[$name][] = array_merge($attributes, $childArray);
+ } else {
+ $result[$name][] = $childArray;
+ }
+ }
+
+ // Extract text from node
+ $text = trim((string) $xml);
+ if ($text === '') {
+ $text = null;
+ }
+
+ // Process attributes
+ $attributes = (array) $xml->attributes($ns, true);
+ if ($attributes) {
+ if ($text !== null) {
+ $result['value'] = $text;
+ }
+ $result = array_merge($attributes, $result);
+ } elseif ($text !== null) {
+ $result = $text;
+ }
+
+ // Make sure we're always returning an array
+ if ($nesting == 0 && !is_array($result)) {
+ $result = [$result];
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaFormatter.php b/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaFormatter.php
new file mode 100644
index 0000000..34f7a88
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaFormatter.php
@@ -0,0 +1,141 @@
+formatDateTime($value);
+ case 'date-time-http':
+ return $this->formatDateTimeHttp($value);
+ case 'date':
+ return $this->formatDate($value);
+ case 'time':
+ return $this->formatTime($value);
+ case 'timestamp':
+ return $this->formatTimestamp($value);
+ case 'boolean-string':
+ return $this->formatBooleanAsString($value);
+ default:
+ return $value;
+ }
+ }
+
+ /**
+ * Perform the actual DateTime formatting
+ *
+ * @param int|string|\DateTime $dateTime Date time value
+ * @param string $format Format of the result
+ *
+ * @return string
+ * @throws \InvalidArgumentException
+ */
+ protected function dateFormatter($dateTime, $format)
+ {
+ if (is_numeric($dateTime)) {
+ return gmdate($format, (int) $dateTime);
+ }
+
+ if (is_string($dateTime)) {
+ $dateTime = new \DateTime($dateTime);
+ }
+
+ if ($dateTime instanceof \DateTimeInterface) {
+ static $utc;
+ if (!$utc) {
+ $utc = new \DateTimeZone('UTC');
+ }
+ return $dateTime->setTimezone($utc)->format($format);
+ }
+
+ throw new \InvalidArgumentException('Date/Time values must be either '
+ . 'be a string, integer, or DateTime object');
+ }
+
+ /**
+ * Create a ISO 8601 (YYYY-MM-DDThh:mm:ssZ) formatted date time value in
+ * UTC time.
+ *
+ * @param string|integer|\DateTime $value Date time value
+ *
+ * @return string
+ */
+ private function formatDateTime($value)
+ {
+ return $this->dateFormatter($value, 'Y-m-d\TH:i:s\Z');
+ }
+
+ /**
+ * Create an HTTP date (RFC 1123 / RFC 822) formatted UTC date-time string
+ *
+ * @param string|integer|\DateTime $value Date time value
+ *
+ * @return string
+ */
+ private function formatDateTimeHttp($value)
+ {
+ return $this->dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
+ }
+
+ /**
+ * Create a YYYY-MM-DD formatted string
+ *
+ * @param string|integer|\DateTime $value Date time value
+ *
+ * @return string
+ */
+ private function formatDate($value)
+ {
+ return $this->dateFormatter($value, 'Y-m-d');
+ }
+
+ /**
+ * Create a hh:mm:ss formatted string
+ *
+ * @param string|integer|\DateTime $value Date time value
+ *
+ * @return string
+ */
+ private function formatTime($value)
+ {
+ return $this->dateFormatter($value, 'H:i:s');
+ }
+
+ /**
+ * Formats a boolean value as a string
+ *
+ * @param string|integer|bool $value Value to convert to a boolean
+ * 'true' / 'false' value
+ *
+ * @return string
+ */
+ private function formatBooleanAsString($value)
+ {
+ return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
+ }
+
+ /**
+ * Return a UNIX timestamp in the UTC timezone
+ *
+ * @param string|integer|\DateTime $value Time value
+ *
+ * @return int
+ */
+ private function formatTimestamp($value)
+ {
+ return (int) $this->dateFormatter($value, 'U');
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaValidator.php b/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaValidator.php
new file mode 100644
index 0000000..4a2833f
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/SchemaValidator.php
@@ -0,0 +1,297 @@
+castIntegerToStringType = $castIntegerToStringType;
+ }
+
+ /**
+ * @param Parameter $param
+ * @param $value
+ * @return bool
+ */
+ public function validate(Parameter $param, &$value)
+ {
+ $this->errors = [];
+ $this->recursiveProcess($param, $value);
+
+ if (empty($this->errors)) {
+ return true;
+ } else {
+ sort($this->errors);
+ return false;
+ }
+ }
+
+ /**
+ * Get the errors encountered while validating
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors ?: [];
+ }
+
+ /**
+ * From the allowable types, determine the type that the variable matches
+ *
+ * @param string|array $type Parameter type
+ * @param mixed $value Value to determine the type
+ *
+ * @return string|false Returns the matching type on
+ */
+ protected function determineType($type, $value)
+ {
+ foreach ((array) $type as $t) {
+ if ($t == 'string'
+ && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))
+ ) {
+ return 'string';
+ } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
+ return 'object';
+ } elseif ($t == 'array' && is_array($value)) {
+ return 'array';
+ } elseif ($t == 'integer' && is_integer($value)) {
+ return 'integer';
+ } elseif ($t == 'boolean' && is_bool($value)) {
+ return 'boolean';
+ } elseif ($t == 'number' && is_numeric($value)) {
+ return 'number';
+ } elseif ($t == 'numeric' && is_numeric($value)) {
+ return 'numeric';
+ } elseif ($t == 'null' && !$value) {
+ return 'null';
+ } elseif ($t == 'any') {
+ return 'any';
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Recursively validate a parameter
+ *
+ * @param Parameter $param API parameter being validated
+ * @param mixed $value Value to validate and validate. The value may
+ * change during this validate.
+ * @param string $path Current validation path (used for error reporting)
+ * @param int $depth Current depth in the validation validate
+ *
+ * @return bool Returns true if valid, or false if invalid
+ */
+ protected function recursiveProcess(
+ Parameter $param,
+ &$value,
+ $path = '',
+ $depth = 0
+ ) {
+ // Update the value by adding default or static values
+ $value = $param->getValue($value);
+
+ $required = $param->isRequired();
+ // if the value is null and the parameter is not required or is static,
+ // then skip any further recursion
+ if ((null === $value && !$required) || $param->isStatic()) {
+ return true;
+ }
+
+ $type = $param->getType();
+ // Attempt to limit the number of times is_array is called by tracking
+ // if the value is an array
+ $valueIsArray = is_array($value);
+ // If a name is set then update the path so that validation messages
+ // are more helpful
+ if ($name = $param->getName()) {
+ $path .= "[{$name}]";
+ }
+
+ if ($type == 'object') {
+ // Determine whether or not this "value" has properties and should
+ // be traversed
+ $traverse = $temporaryValue = false;
+
+ // Convert the value to an array
+ if (!$valueIsArray && $value instanceof ToArrayInterface) {
+ $value = $value->toArray();
+ }
+
+ if ($valueIsArray) {
+ // Ensure that the array is associative and not numerically
+ // indexed
+ if (isset($value[0])) {
+ $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
+ return false;
+ }
+ $traverse = true;
+ } elseif ($value === null) {
+ // Attempt to let the contents be built up by default values if
+ // possible
+ $value = [];
+ $temporaryValue = $valueIsArray = $traverse = true;
+ }
+
+ if ($traverse) {
+ if ($properties = $param->getProperties()) {
+ // if properties were found, validate each property
+ foreach ($properties as $property) {
+ $name = $property->getName();
+ if (isset($value[$name])) {
+ $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
+ } else {
+ $current = null;
+ $this->recursiveProcess($property, $current, $path, $depth + 1);
+ // Only set the value if it was populated
+ if (null !== $current) {
+ $value[$name] = $current;
+ }
+ }
+ }
+ }
+
+ $additional = $param->getAdditionalProperties();
+ if ($additional !== true) {
+ // If additional properties were found, then validate each
+ // against the additionalProperties attr.
+ $keys = array_keys($value);
+ // Determine the keys that were specified that were not
+ // listed in the properties of the schema
+ $diff = array_diff($keys, array_keys($properties));
+ if (!empty($diff)) {
+ // Determine which keys are not in the properties
+ if ($additional instanceof Parameter) {
+ foreach ($diff as $key) {
+ $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
+ }
+ } else {
+ // if additionalProperties is set to false and there
+ // are additionalProperties in the values, then fail
+ foreach ($diff as $prop) {
+ $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop);
+ }
+ }
+ }
+ }
+
+ // A temporary value will be used to traverse elements that
+ // have no corresponding input value. This allows nested
+ // required parameters with default values to bubble up into the
+ // input. Here we check if we used a temp value and nothing
+ // bubbled up, then we need to remote the value.
+ if ($temporaryValue && empty($value)) {
+ $value = null;
+ $valueIsArray = false;
+ }
+ }
+
+ } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
+ foreach ($value as $i => &$item) {
+ // Validate each item in an array against the items attribute of the schema
+ $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
+ }
+ }
+
+ // If the value is required and the type is not null, then there is an
+ // error if the value is not set
+ if ($required && $value === null && $type != 'null') {
+ $message = "{$path} is " . ($param->getType()
+ ? ('a required ' . implode(' or ', (array) $param->getType()))
+ : 'required');
+ if ($param->has('description')) {
+ $message .= ': ' . $param->getDescription();
+ }
+ $this->errors[] = $message;
+ return false;
+ }
+
+ // Validate that the type is correct. If the type is string but an
+ // integer was passed, the class can be instructed to cast the integer
+ // to a string to pass validation. This is the default behavior.
+ if ($type && (!$type = $this->determineType($type, $value))) {
+ if ($this->castIntegerToStringType
+ && $param->getType() == 'string'
+ && is_integer($value)
+ ) {
+ $value = (string) $value;
+ } else {
+ $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
+ }
+ }
+
+ // Perform type specific validation for strings, arrays, and integers
+ if ($type == 'string') {
+ // Strings can have enums which are a list of predefined values
+ if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
+ $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
+ return '"' . addslashes($s) . '"';
+ }, $enum));
+ }
+ // Strings can have a regex pattern that the value must match
+ if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) {
+ $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
+ }
+
+ $strLen = null;
+ if ($min = $param->getMinLength()) {
+ $strLen = strlen($value);
+ if ($strLen < $min) {
+ $this->errors[] = "{$path} length must be greater than or equal to {$min}";
+ }
+ }
+ if ($max = $param->getMaxLength()) {
+ if (($strLen ?: strlen($value)) > $max) {
+ $this->errors[] = "{$path} length must be less than or equal to {$max}";
+ }
+ }
+
+ } elseif ($type == 'array') {
+ $size = null;
+ if ($min = $param->getMinItems()) {
+ $size = count($value);
+ if ($size < $min) {
+ $this->errors[] = "{$path} must contain {$min} or more elements";
+ }
+ }
+ if ($max = $param->getMaxItems()) {
+ if (($size ?: count($value)) > $max) {
+ $this->errors[] = "{$path} must contain {$max} or fewer elements";
+ }
+ }
+
+ } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
+ if (($min = $param->getMinimum()) && $value < $min) {
+ $this->errors[] = "{$path} must be greater than or equal to {$min}";
+ }
+ if (($max = $param->getMaximum()) && $value > $max) {
+ $this->errors[] = "{$path} must be less than or equal to {$max}";
+ }
+ }
+
+ return empty($this->errors);
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/guzzle-services/src/Serializer.php b/server/addons/cos/library/Guzzle/guzzle-services/src/Serializer.php
new file mode 100644
index 0000000..87fbd5b
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/guzzle-services/src/Serializer.php
@@ -0,0 +1,166 @@
+ new BodyLocation(),
+ 'query' => new QueryLocation(),
+ 'header' => new HeaderLocation(),
+ 'json' => new JsonLocation(),
+ 'xml' => new XmlLocation(),
+ 'formParam' => new FormParamLocation(),
+ 'multipart' => new MultiPartLocation(),
+ ];
+ }
+
+ $this->locations = $requestLocations + $defaultRequestLocations;
+ $this->description = $description;
+ }
+
+ /**
+ * @param CommandInterface $command
+ * @return RequestInterface
+ */
+ public function __invoke(CommandInterface $command)
+ {
+ $request = $this->createRequest($command);
+ return $this->prepareRequest($command, $request);
+ }
+
+ /**
+ * Prepares a request for sending using location visitors
+ *
+ * @param CommandInterface $command
+ * @param RequestInterface $request Request being created
+ * @return RequestInterface
+ * @throws \RuntimeException If a location cannot be handled
+ */
+ protected function prepareRequest(
+ CommandInterface $command,
+ RequestInterface $request
+ ) {
+ $visitedLocations = [];
+ $operation = $this->description->getOperation($command->getName());
+
+ // Visit each actual parameter
+ foreach ($operation->getParams() as $name => $param) {
+ /* @var Parameter $param */
+ $location = $param->getLocation();
+ // Skip parameters that have not been set or are URI location
+ if ($location == 'uri' || !$command->hasParam($name)) {
+ continue;
+ }
+ if (!isset($this->locations[$location])) {
+ throw new \RuntimeException("No location registered for $name");
+ }
+ $visitedLocations[$location] = true;
+ $request = $this->locations[$location]->visit($command, $request, $param);
+ }
+
+ // Ensure that the after() method is invoked for additionalParameters
+ /** @var Parameter $additional */
+ if ($additional = $operation->getAdditionalParameters()) {
+ $visitedLocations[$additional->getLocation()] = true;
+ }
+
+ // Call the after() method for each visited location
+ foreach (array_keys($visitedLocations) as $location) {
+ $request = $this->locations[$location]->after($command, $request, $operation);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Create a request for the command and operation
+ *
+ * @param CommandInterface $command
+ *
+ * @return RequestInterface
+ * @throws \RuntimeException
+ */
+ protected function createRequest(CommandInterface $command)
+ {
+ $operation = $this->description->getOperation($command->getName());
+
+ // If command does not specify a template, assume the client's base URL.
+ if (null === $operation->getUri()) {
+ return new Request(
+ $operation->getHttpMethod() ?: 'GET',
+ $this->description->getBaseUri()
+ );
+ }
+
+ return $this->createCommandWithUri($operation, $command);
+ }
+
+ /**
+ * Create a request for an operation with a uri merged onto a base URI
+ *
+ * @param \GuzzleHttp\Command\Guzzle\Operation $operation
+ * @param \GuzzleHttp\Command\CommandInterface $command
+ *
+ * @return \GuzzleHttp\Psr7\Request
+ */
+ private function createCommandWithUri(
+ Operation $operation,
+ CommandInterface $command
+ ) {
+ // Get the path values and use the client config settings
+ $variables = [];
+ foreach ($operation->getParams() as $name => $arg) {
+ /* @var Parameter $arg */
+ if ($arg->getLocation() == 'uri') {
+ if (isset($command[$name])) {
+ $variables[$name] = $arg->filter($command[$name]);
+ if (!is_array($variables[$name])) {
+ $variables[$name] = (string) $variables[$name];
+ }
+ }
+ }
+ }
+
+ // Expand the URI template.
+ $uri = new Uri(UriTemplate::expand($operation->getUri(), $variables));
+
+ return new Request(
+ $operation->getHttpMethod() ?: 'GET',
+ UriResolver::resolve($this->description->getBaseUri(), $uri)
+ );
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/uri-template/CHANGELOG.md b/server/addons/cos/library/Guzzle/uri-template/CHANGELOG.md
new file mode 100644
index 0000000..bf89924
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/uri-template/CHANGELOG.md
@@ -0,0 +1,37 @@
+# Changelog
+
+All notable changes to `uri-template` will be documented in this file.
+
+Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
+
+## v1.0.1 - 2021-10-07
+
+### Changed
+- Officially support PHP 8.1
+
+## v1.0.0 - 2021-08-14
+
+### Changed
+- Dropped support for PHP 7.1
+
+## v0.2.0 - 2020-07-21
+
+### Added
+- Support PHP 7.1 and 8.0
+
+### Changed
+- Renamed `GuzzleHttp\Utility\` to `GuzzleHttp\UriTemplate\`
+
+### Fixed
+- Delegate RFC 3986 query string encoding to PHP
+- Fixed some bugs when parts ofs values are not strings
+
+## v0.1.1 - 2020-06-30
+
+### Fixed
+- Fixed an error due to strict_types [d47d1b0a8e78a3fac1cd0f69d675fc9e06771ac8](https://github.com/guzzle/uri-template/commit/d47d1b0a8e78a3fac1cd0f69d675fc9e06771ac8)
+
+## v0.1.0 - 2020-06-30
+
+### Added
+- Moved the `UriTemplate` class in this package
diff --git a/server/addons/cos/library/Guzzle/uri-template/LICENSE b/server/addons/cos/library/Guzzle/uri-template/LICENSE
new file mode 100644
index 0000000..e70120f
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/uri-template/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Michael Dowling
+Copyright (c) 2020 George Mponos
+Copyright (c) 2020 Graham Campbell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/server/addons/cos/library/Guzzle/uri-template/README.md b/server/addons/cos/library/Guzzle/uri-template/README.md
new file mode 100644
index 0000000..ae187e9
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/uri-template/README.md
@@ -0,0 +1,33 @@
+# uri-template
+
+## Install
+
+Via Composer
+
+``` bash
+$ composer require guzzlehttp/uri-template
+```
+
+## Change log
+
+Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
+
+## Testing
+
+``` bash
+$ make test
+```
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/uri-template/security/policy) for more information.
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-uri-template?utm_source=packagist-guzzlehttp-uri-template7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/server/addons/cos/library/Guzzle/uri-template/composer.json b/server/addons/cos/library/Guzzle/uri-template/composer.json
new file mode 100644
index 0000000..11ffd33
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/uri-template/composer.json
@@ -0,0 +1,71 @@
+{
+ "name": "guzzlehttp/uri-template",
+ "description": "A polyfill class for uri_template of PHP",
+ "keywords": [
+ "guzzlehttp",
+ "uri-template"
+ ],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "repositories": [
+ {
+ "type": "package",
+ "package": {
+ "name": "uri-template/tests",
+ "version": "1.0.0",
+ "dist": {
+ "url": "https://github.com/uri-templates/uritemplate-test/archive/520fdd8b0f78779d12178c357a986e0e727f4bd0.zip",
+ "type": "zip"
+ }
+ }
+ }
+ ],
+ "require": {
+ "php" : "^7.2.5 || ^8.0",
+ "symfony/polyfill-php80": "^1.17"
+ },
+ "require-dev": {
+ "phpunit/phpunit" : "^8.5.19 || ^9.5.8",
+ "uri-template/tests": "1.0.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\UriTemplate\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "GuzzleHttp\\UriTemplate\\Tests\\": "tests"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "config": {
+ "preferred-install": "dist",
+ "sort-packages": true
+ }
+}
diff --git a/server/addons/cos/library/Guzzle/uri-template/src/UriTemplate.php b/server/addons/cos/library/Guzzle/uri-template/src/UriTemplate.php
new file mode 100644
index 0000000..c699164
--- /dev/null
+++ b/server/addons/cos/library/Guzzle/uri-template/src/UriTemplate.php
@@ -0,0 +1,297 @@
+ Hash for quick operator lookups
+ */
+ private static $operatorHash = [
+ '' => ['prefix' => '', 'joiner' => ',', 'query' => false],
+ '+' => ['prefix' => '', 'joiner' => ',', 'query' => false],
+ '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
+ '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
+ '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
+ ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
+ '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
+ '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true],
+ ];
+
+ /**
+ * @var string[] Delimiters
+ */
+ private static $delims = [
+ ':',
+ '/',
+ '?',
+ '#',
+ '[',
+ ']',
+ '@',
+ '!',
+ '$',
+ '&',
+ '\'',
+ '(',
+ ')',
+ '*',
+ '+',
+ ',',
+ ';',
+ '=',
+ ];
+
+ /**
+ * @var string[] Percent encoded delimiters
+ */
+ private static $delimsPct = [
+ '%3A',
+ '%2F',
+ '%3F',
+ '%23',
+ '%5B',
+ '%5D',
+ '%40',
+ '%21',
+ '%24',
+ '%26',
+ '%27',
+ '%28',
+ '%29',
+ '%2A',
+ '%2B',
+ '%2C',
+ '%3B',
+ '%3D',
+ ];
+
+ /**
+ * @param array $variables Variables to use in the template expansion
+ *
+ * @throws \RuntimeException
+ */
+ public static function expand(string $template, array $variables): string
+ {
+ if (false === \strpos($template, '{')) {
+ return $template;
+ }
+
+ /** @var string|null */
+ $result = \preg_replace_callback(
+ '/\{([^\}]+)\}/',
+ self::expandMatchCallback($variables),
+ $template
+ );
+
+ if (null === $result) {
+ throw new \RuntimeException(\sprintf('Unable to process template: %s', \preg_last_error_msg()));
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $variables Variables to use in the template expansion
+ *
+ * @return callable(string[]): string
+ */
+ private static function expandMatchCallback(array $variables): callable
+ {
+ return static function (array $matches) use ($variables): string {
+ return self::expandMatch($matches, $variables);
+ };
+ }
+
+ /**
+ * Process an expansion
+ *
+ * @param array $variables Variables to use in the template expansion
+ * @param string[] $matches Matches met in the preg_replace_callback
+ *
+ * @return string Returns the replacement string
+ */
+ private static function expandMatch(array $matches, array $variables): string
+ {
+ $replacements = [];
+ $parsed = self::parseExpression($matches[1]);
+ $prefix = self::$operatorHash[$parsed['operator']]['prefix'];
+ $joiner = self::$operatorHash[$parsed['operator']]['joiner'];
+ $useQuery = self::$operatorHash[$parsed['operator']]['query'];
+ $allUndefined = true;
+
+ foreach ($parsed['values'] as $value) {
+ if (!isset($variables[$value['value']])) {
+ continue;
+ }
+
+ /** @var mixed */
+ $variable = $variables[$value['value']];
+ $actuallyUseQuery = $useQuery;
+ $expanded = '';
+
+ if (\is_array($variable)) {
+ $isAssoc = self::isAssoc($variable);
+ $kvp = [];
+ /** @var mixed $var */
+ foreach ($variable as $key => $var) {
+ if ($isAssoc) {
+ $key = \rawurlencode((string) $key);
+ $isNestedArray = \is_array($var);
+ } else {
+ $isNestedArray = false;
+ }
+
+ if (!$isNestedArray) {
+ $var = \rawurlencode((string) $var);
+ if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
+ $var = self::decodeReserved($var);
+ }
+ }
+
+ if ($value['modifier'] === '*') {
+ if ($isAssoc) {
+ if ($isNestedArray) {
+ // Nested arrays must allow for deeply nested structures.
+ $var = \http_build_query([$key => $var], '', '&', \PHP_QUERY_RFC3986);
+ } else {
+ $var = \sprintf('%s=%s', (string) $key, (string) $var);
+ }
+ } elseif ($key > 0 && $actuallyUseQuery) {
+ $var = \sprintf('%s=%s', $value['value'], (string) $var);
+ }
+ }
+
+ /** @var string $var */
+
+ $kvp[$key] = $var;
+ }
+
+ if (0 === \count($variable)) {
+ $actuallyUseQuery = false;
+ } elseif ($value['modifier'] === '*') {
+ $expanded = \implode($joiner, $kvp);
+ if ($isAssoc) {
+ // Don't prepend the value name when using the explode
+ // modifier with an associative array.
+ $actuallyUseQuery = false;
+ }
+ } else {
+ if ($isAssoc) {
+ // When an associative array is encountered and the
+ // explode modifier is not set, then the result must be
+ // a comma separated list of keys followed by their
+ // respective values.
+ foreach ($kvp as $k => &$v) {
+ $v = \sprintf('%s,%s', $k, $v);
+ }
+ }
+ $expanded = \implode(',', $kvp);
+ }
+ } else {
+ $allUndefined = false;
+ if ($value['modifier'] === ':' && isset($value['position'])) {
+ $variable = \substr((string) $variable, 0, $value['position']);
+ }
+ $expanded = \rawurlencode((string) $variable);
+ if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
+ $expanded = self::decodeReserved($expanded);
+ }
+ }
+
+ if ($actuallyUseQuery) {
+ if (!$expanded && $joiner !== '&') {
+ $expanded = $value['value'];
+ } else {
+ $expanded = \sprintf('%s=%s', $value['value'], $expanded);
+ }
+ }
+
+ $replacements[] = $expanded;
+ }
+
+ $ret = \implode($joiner, $replacements);
+
+ if ('' === $ret) {
+ // Spec section 3.2.4 and 3.2.5
+ if (false === $allUndefined && ('#' === $prefix || '.' === $prefix)) {
+ return $prefix;
+ }
+ } else {
+ if ('' !== $prefix) {
+ return \sprintf('%s%s', $prefix, $ret);
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Parse an expression into parts
+ *
+ * @param string $expression Expression to parse
+ *
+ * @return array{operator:string, values:array}
+ */
+ private static function parseExpression(string $expression): array
+ {
+ $result = [];
+
+ if (isset(self::$operatorHash[$expression[0]])) {
+ $result['operator'] = $expression[0];
+ /** @var string */
+ $expression = \substr($expression, 1);
+ } else {
+ $result['operator'] = '';
+ }
+
+ $result['values'] = [];
+ foreach (\explode(',', $expression) as $value) {
+ $value = \trim($value);
+ $varspec = [];
+ if ($colonPos = \strpos($value, ':')) {
+ $varspec['value'] = (string) \substr($value, 0, $colonPos);
+ $varspec['modifier'] = ':';
+ $varspec['position'] = (int) \substr($value, $colonPos + 1);
+ } elseif (\substr($value, -1) === '*') {
+ $varspec['modifier'] = '*';
+ $varspec['value'] = (string) \substr($value, 0, -1);
+ } else {
+ $varspec['value'] = $value;
+ $varspec['modifier'] = '';
+ }
+ $result['values'][] = $varspec;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Determines if an array is associative.
+ *
+ * This makes the assumption that input arrays are sequences or hashes.
+ * This assumption is a tradeoff for accuracy in favor of speed, but it
+ * should work in almost every case where input is supplied for a URI
+ * template.
+ */
+ private static function isAssoc(array $array): bool
+ {
+ return $array && \array_keys($array)[0] !== 0;
+ }
+
+ /**
+ * Removes percent encoding on reserved characters (used with + and #
+ * modifiers).
+ */
+ private static function decodeReserved(string $string): string
+ {
+ return \str_replace(self::$delimsPct, self::$delims, $string);
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Client.php b/server/addons/cos/library/Qcloud/Cos/Client.php
new file mode 100644
index 0000000..4a98c59
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Client.php
@@ -0,0 +1,569 @@
+rawCosConfig = $cosConfig;
+ $this->cosConfig['schema'] = isset($cosConfig['schema']) ? $cosConfig['schema'] : 'http';
+ $this->cosConfig['region'] = isset($cosConfig['region']) ? region_map($cosConfig['region']) : null;
+ $this->cosConfig['appId'] = isset($cosConfig['credentials']['appId']) ? $cosConfig['credentials']['appId'] : null;
+ $this->cosConfig['secretId'] = isset($cosConfig['credentials']['secretId']) ? trim($cosConfig['credentials']['secretId']) : '';
+ $this->cosConfig['secretKey'] = isset($cosConfig['credentials']['secretKey']) ? trim($cosConfig['credentials']['secretKey']) : '';
+ $this->cosConfig['anonymous'] = isset($cosConfig['credentials']['anonymous']) ? $cosConfig['credentials']['anonymous'] : false;
+ $this->cosConfig['token'] = isset($cosConfig['credentials']['token']) ? trim($cosConfig['credentials']['token']) : null;
+ $this->cosConfig['timeout'] = isset($cosConfig['timeout']) ? $cosConfig['timeout'] : 3600;
+ $this->cosConfig['connect_timeout'] = isset($cosConfig['connect_timeout']) ? $cosConfig['connect_timeout'] : 3600;
+ $this->cosConfig['ip'] = isset($cosConfig['ip']) ? $cosConfig['ip'] : null;
+ $this->cosConfig['port'] = isset($cosConfig['port']) ? $cosConfig['port'] : null;
+ $this->cosConfig['endpoint'] = isset($cosConfig['endpoint']) ? $cosConfig['endpoint'] : null;
+ $this->cosConfig['domain'] = isset($cosConfig['domain']) ? $cosConfig['domain'] : null;
+ $this->cosConfig['proxy'] = isset($cosConfig['proxy']) ? $cosConfig['proxy'] : null;
+ $this->cosConfig['retry'] = isset($cosConfig['retry']) ? $cosConfig['retry'] : 1;
+ $this->cosConfig['userAgent'] = isset($cosConfig['userAgent']) ? $cosConfig['userAgent'] : 'cos-php-sdk-v5.'. Client::VERSION;
+ $this->cosConfig['pathStyle'] = isset($cosConfig['pathStyle']) ? $cosConfig['pathStyle'] : false;
+ $this->cosConfig['signHost'] = isset($cosConfig['signHost']) ? $cosConfig['signHost'] : true;
+ $this->cosConfig['allow_redirects'] = isset($cosConfig['allow_redirects']) ? $cosConfig['allow_redirects'] : false;
+ $this->cosConfig['allow_accelerate'] = isset($cosConfig['allow_accelerate']) ? $cosConfig['allow_accelerate'] : false;
+
+ // check config
+ $this->inputCheck();
+
+ $service = Service::getService();
+ $handler = HandlerStack::create();
+ $handler->push(Middleware::retry($this->retryDecide(), $this->retryDelay()));
+ $handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+ return $request->withHeader('User-Agent', $this->cosConfig['userAgent']);
+ }));
+ if ($this->cosConfig['anonymous'] != true) {
+ $handler->push($this::handleSignature($this->cosConfig['secretId'], $this->cosConfig['secretKey'], $this->cosConfig));
+ }
+ if ($this->cosConfig['token'] != null) {
+ $handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+ return $request->withHeader('x-cos-security-token', $this->cosConfig['token']);
+ }));
+ }
+ $handler->push($this::handleErrors());
+ $this->signature = new Signature($this->cosConfig['secretId'], $this->cosConfig['secretKey'], $this->cosConfig, $this->cosConfig['token']);
+ $area = $this->cosConfig['allow_accelerate'] ? 'accelerate' : $this->cosConfig['region'];
+ $this->httpClient = new HttpClient([
+ 'base_uri' => "{$this->cosConfig['schema']}://cos.{$area}.myqcloud.com/",
+ 'timeout' => $this->cosConfig['timeout'],
+ 'handler' => $handler,
+ 'proxy' => $this->cosConfig['proxy'],
+ 'allow_redirects' => $this->cosConfig['allow_redirects']
+ ]);
+ $this->desc = new Description($service);
+ $this->api = (array) $this->desc->getOperations();
+ parent::__construct($this->httpClient, $this->desc, [$this,
+ 'commandToRequestTransformer'], [$this, 'responseToResultTransformer'],
+ null);
+ }
+
+ public function inputCheck() {
+ $message = null;
+ //检查Region
+ if (empty($this->cosConfig['region']) &&
+ empty($this->cosConfig['domain']) &&
+ empty($this->cosConfig['endpoint']) &&
+ empty($this->cosConfig['ip']) &&
+ !$this->cosConfig['allow_accelerate']) {
+ $message = 'Region is empty';
+ }
+ //检查Secret
+ if (empty($this->cosConfig['secretId']) || empty($this->cosConfig['secretKey'])) {
+ $message = 'Secret is empty';
+ }
+ if ($message !== null) {
+ $e = new Exception\CosException($message);
+ $e->setExceptionCode('Invalid Argument');
+ throw $e;
+ }
+ }
+
+
+ public function retryDecide() {
+ return function (
+ $retries,
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ \Exception $exception = null
+ ) {
+ if ($retries >= $this->cosConfig['retry']) {
+ return false;
+ }
+ if ($response != null && $response->getStatusCode() >= 400 ) {
+ return true;
+ }
+ if ($exception instanceof Exception\ServiceResponseException) {
+ if ($exception->getStatusCode() >= 400) {
+ return true;
+ }
+ }
+
+ if ($exception instanceof ConnectException) {
+ return true;
+ }
+
+ return false;
+ };
+ }
+
+ public function retryDelay() {
+ return function ($numberOfRetries) {
+ return 1000 * $numberOfRetries;
+ };
+ }
+
+ public function commandToRequestTransformer(CommandInterface $command)
+ {
+ $this->action = $command->GetName();
+ $this->operation = $this->api[$this->action];
+ $transformer = new CommandToRequestTransformer($this->cosConfig, $this->operation);
+ $seri = new Serializer($this->desc);
+ $request = $seri($command);
+ $request = $transformer->bucketStyleTransformer($command, $request);
+ $request = $transformer->uploadBodyTransformer($command, $request);
+ $request = $transformer->metadataTransformer($command, $request);
+ $request = $transformer->queryStringTransformer($command, $request);
+ $request = $transformer->headerTransformer($command, $request);
+ $request = $transformer->md5Transformer($command, $request);
+ $request = $transformer->specialParamTransformer($command, $request);
+ $request = $transformer->ciParamTransformer($command, $request);
+ $request = $transformer->cosDomain2CiTransformer($command, $request);
+ return $request;
+ }
+
+ public function responseToResultTransformer(ResponseInterface $response, RequestInterface $request, CommandInterface $command)
+ {
+ $transformer = new ResultTransformer($this->cosConfig, $this->operation);
+ $transformer->writeDataToLocal($command, $request, $response);
+ $deseri = new Deserializer($this->desc, true);
+ $result = $deseri($response, $request, $command);
+
+ $result = $transformer->metaDataTransformer($command, $response, $result);
+ $result = $transformer->extraHeadersTransformer($command, $request, $result);
+ $result = $transformer->selectContentTransformer($command, $result);
+ $result = $transformer->ciContentInfoTransformer($command, $result);
+ return $result;
+ }
+
+ public function __destruct() {
+ }
+
+ public function __call($method, array $args) {
+ try {
+ $rt = parent::__call(ucfirst($method), $args);
+ return $rt;
+ } catch (\Exception $e) {
+ $previous = $e->getPrevious();
+ if ($previous !== null) {
+ throw $previous;
+ } else {
+ throw $e;
+ }
+ }
+ }
+
+ public function getApi() {
+ return $this->api;
+ }
+
+ private function getCosConfig() {
+ return $this->cosConfig;
+ }
+
+ private function createPresignedUrl(RequestInterface $request, $expires) {
+ return $this->signature->createPresignedUrl($request, $expires);
+ }
+
+ public function getPresignedUrl($method, $args, $expires = "+30 minutes") {
+ $command = $this->getCommand($method, $args);
+ $request = $this->commandToRequestTransformer($command);
+ return $this->createPresignedUrl($request, $expires);
+ }
+
+
+ public function getObjectUrl($bucket, $key, $expires = "+30 minutes", array $args = array()) {
+ $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key));
+ $request = $this->commandToRequestTransformer($command);
+ return $this->createPresignedUrl($request, $expires)->__toString();
+ }
+
+ public function getObjectUrlWithoutSign($bucket, $key, array $args = array()) {
+ $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key));
+ $request = $this->commandToRequestTransformer($command);
+ return $request->getUri()-> __toString();
+ }
+
+ public function upload($bucket, $key, $body, $options = array()) {
+ $body = Psr7\Utils::streamFor($body);
+ $options['Retry'] = $this->cosConfig['retry'];
+ $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : MultipartUpload::DEFAULT_PART_SIZE;
+ if ($body->getSize() < $options['PartSize']) {
+ $rt = $this->putObject(array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ 'Body' => $body,
+ ) + $options);
+ }
+ else {
+ $multipartUpload = new MultipartUpload($this, $body, array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ ) + $options);
+
+ $rt = $multipartUpload->performUploading();
+ }
+ return $rt;
+ }
+
+ public function download($bucket, $key, $saveAs, $options = array()) {
+ $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : RangeDownload::DEFAULT_PART_SIZE;
+ $contentLength = 0;
+ $versionId = isset($options['VersionId']) ? $options['VersionId'] : '';
+
+ $rt = $this->headObject(array(
+ 'Bucket'=>$bucket,
+ 'Key'=>$key,
+ 'VersionId'=>$versionId,
+ )
+ );
+ $contentLength = $rt['ContentLength'];
+ $resumableJson = [
+ 'LastModified' => $rt['LastModified'],
+ 'ContentLength' => $rt['ContentLength'],
+ 'ETag' => $rt['ETag'],
+ 'Crc64ecma' => $rt['Crc64ecma']
+ ];
+ $options['ResumableJson'] = $resumableJson;
+
+ if ($contentLength < $options['PartSize']) {
+ $rt = $this->getObject(array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ 'SaveAs' => $saveAs,
+ ) + $options);
+ } else {
+ $rangeDownload = new RangeDownload($this, $contentLength, $saveAs, array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ ) + $options);
+
+ $rt = $rangeDownload->performDownloading();
+ }
+ return $rt;
+ }
+
+ public function resumeUpload($bucket, $key, $body, $uploadId, $options = array()) {
+ $body = Psr7\Utils::streamFor($body);
+ $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : MultipartUpload::DEFAULT_PART_SIZE;
+ $multipartUpload = new MultipartUpload($this, $body, array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ 'UploadId' => $uploadId,
+ ) + $options);
+
+ $rt = $multipartUpload->resumeUploading();
+ return $rt;
+ }
+
+ public function copy($bucket, $key, $copySource, $options = array()) {
+
+ $options['PartSize'] = isset($options['PartSize']) ? $options['PartSize'] : Copy::DEFAULT_PART_SIZE;
+
+ // set copysource client
+ $sourceConfig = $this->rawCosConfig;
+ $sourceConfig['region'] = $copySource['Region'];
+ $cosSourceClient = new Client($sourceConfig);
+ $copySource['VersionId'] = isset($copySource['VersionId']) ? $copySource['VersionId'] : '';
+
+ $rt = $cosSourceClient->headObject(
+ array('Bucket'=>$copySource['Bucket'],
+ 'Key'=>$copySource['Key'],
+ 'VersionId'=>$copySource['VersionId'],
+ )
+ );
+
+ $contentLength = $rt['ContentLength'];
+ // sample copy
+ if ($contentLength < $options['PartSize']) {
+ $rt = $this->copyObject(array(
+ 'Bucket' => $bucket,
+ 'Key' => $key,
+ 'CopySource' => "{$copySource['Bucket']}.cos.{$copySource['Region']}.myqcloud.com/". urlencode("{$copySource['Key']}")."?versionId={$copySource['VersionId']}",
+ ) + $options
+ );
+ return $rt;
+ }
+ // multi part copy
+ $copySource['ContentLength'] = $contentLength;
+ $copy = new Copy($this, $copySource, array(
+ 'Bucket' => $bucket,
+ 'Key' => $key
+ ) + $options
+ );
+ return $copy->copy();
+ }
+
+ public function doesBucketExist($bucket, array $options = array())
+ {
+ try {
+ $this->HeadBucket(array(
+ 'Bucket' => $bucket));
+ return true;
+ } catch (\Exception $e){
+ return false;
+ }
+ }
+
+ public function doesObjectExist($bucket, $key, array $options = array())
+ {
+ try {
+ $this->HeadObject(array(
+ 'Bucket' => $bucket,
+ 'Key' => $key));
+ return true;
+ } catch (\Exception $e){
+ return false;
+ }
+ }
+
+ public static function explodeKey($key) {
+ // Remove a leading slash if one is found
+ $split_key = explode('/', $key && $key[0] == '/' ? substr($key, 1) : $key);
+ // Remove empty element
+ $split_key = array_filter($split_key, function($var) {
+ return !($var == '' || $var == null);
+ });
+ $final_key = implode("/", $split_key);
+ if (substr($key, -1) == '/') {
+ $final_key = $final_key . '/';
+ }
+ return $final_key;
+ }
+
+
+ public static function handleSignature($secretId, $secretKey, $options) {
+ return function (callable $handler) use ($secretId, $secretKey, $options) {
+ return new SignatureMiddleware($handler, $secretId, $secretKey, $options);
+ };
+ }
+
+ public static function handleErrors() {
+ return function (callable $handler) {
+ return new ExceptionMiddleware($handler);
+ };
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/CommandToRequestTransformer.php b/server/addons/cos/library/Qcloud/Cos/CommandToRequestTransformer.php
new file mode 100644
index 0000000..6953b66
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/CommandToRequestTransformer.php
@@ -0,0 +1,370 @@
+config = $config;
+ $this->operation = $operation;
+ }
+
+ // format bucket style
+
+ public function bucketStyleTransformer( CommandInterface $command, RequestInterface $request ) {
+ $action = $command->getName();
+ if ($action == 'ListBuckets') {
+ $uri = "service.cos.myqcloud.com";
+
+ if ($this->config['endpoint'] != null) {
+ $uri = $this->config['endpoint'];
+ }
+ if ($this->config['domain'] != null) {
+ $uri = $this->config['domain'];
+ }
+ if ($this->config['ip'] != null) {
+ $uri = $this->config['ip'];
+ if ($this->config['port'] != null) {
+ $uri = $this->config['ip'] . ":" . $this->config['port'];
+ }
+ }
+ return $request->withUri(new Uri($this->config['schema']."://". $uri. "/"));
+ }
+ $operation = $this->operation;
+ $bucketname = $command['Bucket'];
+
+ $appId = $this->config['appId'];
+ if ( $appId != null && endWith( $bucketname, '-'.$appId ) == false ) {
+ $bucketname = $bucketname.'-'.$appId;
+ }
+ $command['Bucket'] = $bucketname;
+
+ $uri = $operation['uri'];
+
+ // Hoststyle is used by default
+ // Pathstyle
+ if ( $this->config['pathStyle'] != true ) {
+ if ( isset( $operation['parameters']['Bucket'] ) && $command->hasParam( 'Bucket' ) ) {
+ $uri = str_replace( '{Bucket}', '', $uri );
+ }
+
+ if ( isset( $operation['parameters']['Key'] ) && $command->hasParam( 'Key' ) ) {
+ $uri = str_replace( '{/Key*}', encodeKey( $command['Key'] ), $uri );
+ }
+ }
+
+ if ($this->config['endpoint'] == null) {
+ $this->config['endpoint'] = "myqcloud.com";
+ }
+
+ $domain_type = '.cos.';
+ if ($action == 'PutBucketImageStyle' || $action == 'GetBucketImageStyle' || $action == 'DeleteBucketImageStyle'
+ || $action == 'PutBucketGuetzli' || $action == 'GetBucketGuetzli' || $action == 'DeleteBucketGuetzli'
+ || $action == 'BindCiService' || $action == 'GetCiService' || $action == 'UnBindCiService'
+ || $action == 'GetHotLink' || $action == 'AddHotLink'
+ || $action == 'OpenOriginProtect' || $action == 'GetOriginProtect' || $action == 'CloseOriginProtect') {
+ $domain_type = '.pic.';
+ }
+
+ $origin_host = $this->config['allow_accelerate'] ?
+ $bucketname . $domain_type . 'accelerate' . '.' . $this->config['endpoint'] :
+ $bucketname . $domain_type . $this->config['region'] . '.' . $this->config['endpoint'];
+
+ // domain
+ if ( $this->config['domain'] != null ) {
+ $origin_host = $this->config['domain'];
+ }
+ $host = $origin_host;
+ if ( $this->config['ip'] != null ) {
+ $host = $this->config['ip'];
+ if ( $this->config['port'] != null ) {
+ $host = $this->config['ip'] . ':' . $this->config['port'];
+ }
+ }
+
+ $path = $this->config['schema'].'://'. $host . $uri;
+ $uri = new Uri( $path );
+ $query = $request->getUri()->getQuery();
+ if ( $uri->getQuery() != $query && $uri->getQuery() != '' ) {
+ $query = $uri->getQuery() . '&' . $request->getUri()->getQuery();
+ }
+ $uri = $uri->withQuery( $query );
+ $request = $request->withUri( $uri );
+ $request = $request->withHeader( 'Host', $origin_host );
+ return $request;
+ }
+
+ // format upload body
+
+ public function uploadBodyTransformer( CommandInterface $command, $request, $bodyParameter = 'Body', $sourceParameter = 'SourceFile' ) {
+
+ $operation = $this->operation;
+ if ( !isset( $operation['parameters']['Body'] ) ) {
+ return $request;
+ }
+ $source = isset( $command[$sourceParameter] ) ? $command[$sourceParameter] : null;
+ $body = isset( $command[$bodyParameter] ) ? $command[$bodyParameter] : null;
+ // If a file path is passed in then get the file handle
+ if ( is_string( $source ) && file_exists( $source ) ) {
+ $body = fopen( $source, 'rb' );
+ }
+ // Prepare the body parameter and remove the source file parameter
+ if ( null !== $body ) {
+ return $request;
+ } else {
+ throw new InvalidArgumentException(
+ "You must specify a non-null value for the {$bodyParameter} or {$sourceParameter} parameters." );
+ }
+ }
+
+ // update md5
+
+ public function md5Transformer( CommandInterface $command, $request ) {
+ $operation = $this->operation;
+ if ( isset( $operation['data']['contentMd5'] ) ) {
+ $request = $this->addMd5( $request );
+ }
+ if ( isset( $operation['parameters']['ContentMD5'] ) &&
+ isset( $command['ContentMD5'] ) ) {
+ $value = $command['ContentMD5'];
+ if ( $value != false ) {
+ $request = $this->addMd5( $request );
+ }
+ }
+
+ return $request;
+ }
+
+ // add Query string
+
+ public function queryStringTransformer( CommandInterface $command, $request ) {
+ $operation = $this->operation;
+ if ( isset( $command['Params'] ) ) {
+ $params = $command['Params'];
+ foreach ( $params as $key => $value ) {
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+ $uri = $uri->withQuery($query. "&" . urlencode($key) . "=" . $value );
+ $request = $request->withUri( $uri );
+ }
+ }
+
+ return $request;
+ }
+
+ // add Header string
+
+ public function headerTransformer( CommandInterface $command, $request ) {
+ $operation = $this->operation;
+ if ( isset( $command['Headers'] ) ) {
+ $headers = $command['Headers'];
+ foreach ( $headers as $key => $value ) {
+ $request = $request->withHeader( $key, $value);
+ }
+ }
+ return $request;
+ }
+
+ // add meta
+
+ public function metadataTransformer( CommandInterface $command, $request ) {
+ $operation = $this->operation;
+ if ( isset( $command['Metadata'] ) ) {
+ $meta = $command['Metadata'];
+ foreach ( $meta as $key => $value ) {
+ $request = $request->withHeader( 'x-cos-meta-' . $key, $value );
+ }
+ }
+ $request = headersMap( $command, $request );
+
+ return $request;
+ }
+
+ // count md5
+
+ private function addMd5( $request ) {
+ $body = $request->getBody();
+ if ( $body && $body->getSize() > 0 ) {
+ $md5 = base64_encode( md5( $body, true ) );
+ return $request->withHeader( 'Content-MD5', $md5 );
+ }
+ return $request;
+ }
+
+ // inventoryId
+
+ public function specialParamTransformer( CommandInterface $command, $request ) {
+ $action = $command->getName();
+ if ( $action == 'PutBucketInventory' ) {
+ $id = $command['Id'];
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+ $uri = $uri->withQuery( $query . '&Id='.$id );
+ return $request->withUri( $uri );
+ }
+ return $request;
+ }
+
+ public function ciParamTransformer( CommandInterface $command, $request ) {
+ $action = $command->getName();
+ if ( $action == 'GetObject' ) {
+ if(str_contains($uri = $request->getUri(), '%21') ) {
+ $uri = new Uri( str_replace('%21', '!', $uri) );
+ $request = $request->withUri( $uri );
+ }
+ if(isset($command['ImageHandleParam']) && $command['ImageHandleParam']){
+ $uri = $request->getUri();
+ $query = $uri->getQuery();
+ if($query){
+ $query .= "&" . urlencode($command['ImageHandleParam']);
+ }else{
+ $query .= urlencode($command['ImageHandleParam']);
+ }
+ $uri = $uri->withQuery($query);
+ $request = $request->withUri( $uri );
+ }
+ }
+ return $request;
+ }
+
+ public function cosDomain2CiTransformer(CommandInterface $command, $request) {
+ $action = $command->getName();
+ if(key_exists($action, array(
+ 'DescribeMediaBuckets' => 1,
+ 'DescribeDocProcessBuckets' =>1,
+ ))) {
+ $origin_host = "ci.{$this->config['region']}.myqcloud.com";
+ $host = $origin_host;
+ if ($this->config['ip'] != null) {
+ $host = $this->config['ip'];
+ if ($this->config['port'] != null) {
+ $host = $this->config['ip'] . ":" . $this->config['port'];
+ }
+ }
+
+ $path = $this->config['schema'].'://'. $host . $request->getUri()->getPath();
+ $uri = new Uri( $path );
+ $query = $request->getUri()->getQuery();
+ $uri = $uri->withQuery( $query );
+ $request = $request->withUri( $uri );
+ $request = $request->withHeader( 'Host', $origin_host );
+ return $request;
+ }
+ $ciActions = array(
+ 'DetectText' => 1,
+ 'CreateMediaTranscodeJobs' => 1,
+ 'CreateMediaJobs' => 1,
+ 'DescribeMediaJob' => 1,
+ 'DescribeMediaJobs' => 1,
+ 'CreateMediaSnapshotJobs' => 1,
+ 'CreateMediaConcatJobs' => 1,
+ 'DetectAudio' => 1,
+ 'GetDetectAudioResult' => 1,
+ 'GetDetectTextResult' => 1,
+ 'DetectVideo' => 1,
+ 'GetDetectVideoResult' => 1,
+ 'DetectDocument' => 1,
+ 'GetDetectDocumentResult' => 1,
+ 'CreateDocProcessJobs' => 1,
+ 'DescribeDocProcessQueues' => 1,
+ 'DescribeDocProcessJob' => 1,
+ 'GetDescribeDocProcessJobs' => 1,
+ 'DetectImages' => 1,
+ 'GetDetectImageResult' => 1,
+ 'DetectVirus' => 1,
+ 'GetDetectVirusResult' => 1,
+ 'CreateMediaVoiceSeparateJobs' => 1,
+ 'DescribeMediaVoiceSeparateJob' => 1,
+ 'DetectWebpage' => 1,
+ 'GetDetectWebpageResult' => 1,
+ 'DescribeMediaQueues' => 1,
+ 'UpdateMediaQueue' => 1,
+ 'CreateMediaSmartCoverJobs' => 1,
+ 'CreateMediaVideoProcessJobs' => 1,
+ 'CreateMediaVideoMontageJobs' => 1,
+ 'CreateMediaAnimationJobs' => 1,
+ 'CreateMediaPicProcessJobs' => 1,
+ 'CreateMediaSegmentJobs' => 1,
+ 'CreateMediaVideoTagJobs' => 1,
+ 'CreateMediaSuperResolutionJobs' => 1,
+ 'CreateMediaSDRtoHDRJobs' => 1,
+ 'CreateMediaDigitalWatermarkJobs' => 1,
+ 'CreateMediaExtractDigitalWatermarkJobs' => 1,
+ 'DetectLiveVideo' => 1,
+ 'CancelLiveVideoAuditing' => 1,
+ 'TriggerWorkflow' => 1,
+ 'GetWorkflowInstances' => 1,
+ 'GetWorkflowInstance' => 1,
+ 'CreateMediaSnapshotTemplate' => 1,
+ 'UpdateMediaSnapshotTemplate' => 1,
+ 'CreateMediaTranscodeTemplate' => 1,
+ 'UpdateMediaTranscodeTemplate' => 1,
+ 'CreateMediaHighSpeedHdTemplate' => 1,
+ 'UpdateMediaHighSpeedHdTemplate' => 1,
+ 'CreateMediaAnimationTemplate' => 1,
+ 'UpdateMediaAnimationTemplate' => 1,
+ 'CreateMediaConcatTemplate' => 1,
+ 'UpdateMediaConcatTemplate' => 1,
+ 'CreateMediaVideoProcessTemplate' => 1,
+ 'UpdateMediaVideoProcessTemplate' => 1,
+ 'CreateMediaVideoMontageTemplate' => 1,
+ 'UpdateMediaVideoMontageTemplate' => 1,
+ 'CreateMediaVoiceSeparateTemplate' => 1,
+ 'UpdateMediaVoiceSeparateTemplate' => 1,
+ 'CreateMediaSuperResolutionTemplate' => 1,
+ 'UpdateMediaSuperResolutionTemplate' => 1,
+ 'CreateMediaPicProcessTemplate' => 1,
+ 'UpdateMediaPicProcessTemplate' => 1,
+ 'CreateMediaWatermarkTemplate' => 1,
+ 'UpdateMediaWatermarkTemplate' => 1,
+ 'DescribeMediaTemplates' => 1,
+ 'DescribeWorkflow' => 1,
+ 'DeleteWorkflow' => 1,
+ 'CreateInventoryTriggerJob' => 1,
+ 'DescribeInventoryTriggerJobs' => 1,
+ 'DescribeInventoryTriggerJob' => 1,
+ 'CancelInventoryTriggerJob' => 1,
+ 'CreateMediaNoiseReductionJobs' => 1,
+ 'ImageSearchOpen' => 1,
+ 'UpdateDocProcessQueue' => 1,
+ 'CreateMediaQualityEstimateJobs' => 1,
+ 'CreateMediaStreamExtractJobs' => 1,
+ );
+ if (key_exists($action, $ciActions)) {
+ $bucketname = $command['Bucket'];
+ $appId = $this->config['appId'];
+ if ( $appId != null && endWith( $bucketname, '-'.$appId ) == false ) {
+ $bucketname = $bucketname.'-'.$appId;
+ }
+ $command['Bucket'] = $bucketname;
+ $domain_type = '.ci.';
+ $origin_host = $bucketname . $domain_type . $this->config['region'] . '.' . $this->config['endpoint'];
+ $host = $origin_host;
+ if ( $this->config['ip'] != null ) {
+ $host = $this->config['ip'];
+ if ( $this->config['port'] != null ) {
+ $host = $this->config['ip'] . ':' . $this->config['port'];
+ }
+ }
+ $path = $this->config['schema'].'://'. $host . $request->getUri()->getPath();
+ $uri = new Uri( $path );
+ $query = $request->getUri()->getQuery();
+ $uri = $uri->withQuery( $query );
+ $request = $request->withUri( $uri );
+ $request = $request->withHeader( 'Host', $origin_host );
+ }
+ return $request;
+ }
+
+ public function __destruct() {
+ }
+
+ }
diff --git a/server/addons/cos/library/Qcloud/Cos/Common.php b/server/addons/cos/library/Qcloud/Cos/Common.php
new file mode 100644
index 0000000..1cc3246
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Common.php
@@ -0,0 +1,69 @@
+ 'ap-shanghai',
+ 'cn-south' => 'ap-guangzhou',
+ 'cn-north' => 'ap-beijing-1',
+ 'cn-south-2' => 'ap-guangzhou-2',
+ 'cn-southwest' => 'ap-chengdu',
+ 'sg' => 'ap-singapore',
+ 'tj' => 'ap-beijing-1',
+ 'bj' => 'ap-beijing',
+ 'sh' => 'ap-shanghai',
+ 'gz' => 'ap-guangzhou',
+ 'cd' => 'ap-chengdu',
+ 'sgp' => 'ap-singapore'
+ );
+ if (isset($regionmap[$region])) {
+ return $regionmap[$region];
+ }
+ return $region;
+}
+
+function encodeKey($key)
+{
+ return str_replace('%2F', '/', rawurlencode($key));
+}
+
+function endWith($haystack, $needle)
+{
+ $length = strlen($needle);
+ if ($length == 0) {
+ return true;
+ }
+ return (substr($haystack, -$length) === $needle);
+}
+
+function startWith($haystack, $needle)
+{
+ $length = strlen($needle);
+ if ($length == 0) {
+ return true;
+ }
+ return (substr($haystack, 0, $length) === $needle);
+}
+
+function headersMap($command, $request)
+{
+ $headermap = array(
+ 'TransferEncoding' => 'Transfer-Encoding',
+ 'ChannelId' => 'x-cos-channel-id'
+ );
+ foreach ($headermap as $key => $value) {
+ if (isset($command[$key])) {
+ $request = $request->withHeader($value, $command[$key]);
+ }
+ }
+ return $request;
+}
+
+if (!function_exists('str_contains')) {
+ function str_contains($haystack, $needle)
+ {
+ return strpos($haystack, $needle) !== false;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Copy.php b/server/addons/cos/library/Qcloud/Cos/Copy.php
new file mode 100644
index 0000000..18b3d03
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Copy.php
@@ -0,0 +1,140 @@
+client = $client;
+ $this->copySource = $source;
+ $this->options = $options;
+ $this->size = $source['ContentLength'];
+ unset($source['ContentLength']);
+ $this->partSize = $this->calculatePartSize($minPartSize);
+ $this->concurrency = isset($options['Concurrency']) ? $options['Concurrency'] : 10;
+ $this->retry = isset($options['Retry']) ? $options['Retry'] : 5;
+ }
+ public function copy() {
+ $uploadId= $this->initiateMultipartUpload();
+ for ($i = 0; $i < $this->retry; $i += 1) {
+ $rt = $this->uploadParts($uploadId);
+ if ($rt == 0) {
+ break;
+ }
+ sleep(1 << $i);
+ }
+ foreach ( $this->parts as $key => $row ){
+ $num1[$key] = $row ['PartNumber'];
+ $num2[$key] = $row ['ETag'];
+ }
+ array_multisort($num1, SORT_ASC, $num2, SORT_ASC, $this->parts);
+ return $this->client->completeMultipartUpload(array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'UploadId' => $uploadId,
+ 'Parts' => $this->parts)
+ );
+
+ }
+ public function uploadParts($uploadId) {
+ $copyRequests = function ($uploadId) {
+ $offset = 0;
+ $partNumber = 1;
+ $partSize = $this->partSize;
+ $finishedNum = 0;
+ $this->parts = array();
+ for ($index = 1; ; $index ++) {
+ if ($offset + $partSize >= $this->size)
+ {
+ $partSize = $this->size - $offset;
+ }
+ $copySourcePath = $this->copySource['Bucket']. '.cos.'. $this->copySource['Region'].
+ ".myqcloud.com/". $this->copySource['Key']. "?versionId=". $this->copySource['VersionId'];
+ $params = array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'UploadId' => $uploadId,
+ 'PartNumber' => $partNumber,
+ 'CopySource'=> $copySourcePath,
+ 'CopySourceRange' => 'bytes='.((string)$offset).'-'.(string)($offset+$partSize - 1),
+ );
+ if(!isset($this->parts[$partNumber])) {
+ $command = $this->client->getCommand('uploadPartCopy', $params);
+ $request = $this->client->commandToRequestTransformer($command);
+ $this->commandList[$index] = $command;
+ $this->requestList[$index] = $request;
+ yield $request;
+ }
+ ++$partNumber;
+ $offset += $partSize;
+ if ($this->size == $offset) {
+ break;
+ }
+ }
+ };
+ $pool = new Pool($this->client->httpClient, $copyRequests($uploadId), [
+ 'concurrency' => $this->concurrency,
+ 'fulfilled' => function ($response, $index) {
+ $index = $index + 1;
+ $response = $this->client->responseToResultTransformer($response, $this->requestList[$index], $this->commandList[$index]);
+ $part = array('PartNumber' => $index, 'ETag' => $response['ETag']);
+ $this->parts[$index] = $part;
+ },
+
+ 'rejected' => function ($reason, $index) {
+ $index = $index += 1;
+ $retry = 2;
+ for ($i = 1; $i <= $retry; $i++) {
+ try {
+ $rt =$this->client->execute($this->commandList[$index]);
+ $part = array('PartNumber' => $index, 'ETag' => $rt['ETag']);
+ $this->parts[$index] = $part;
+ } catch(\Exception $e) {
+ if ($i == $retry) {
+ throw($e);
+ }
+ }
+ }
+ },
+ ]);
+
+ // Initiate the transfers and create a promise
+ $promise = $pool->promise();
+
+ // Force the pool of requests to complete.
+ $promise->wait();
+ }
+
+
+ private function calculatePartSize($minPartSize)
+ {
+ $partSize = intval(ceil(($this->size / self::MAX_PARTS)));
+ $partSize = max($minPartSize, $partSize);
+ $partSize = min($partSize, self::MAX_PART_SIZE);
+ $partSize = max($partSize, self::MIN_PART_SIZE);
+ return $partSize;
+ }
+
+ private function initiateMultipartUpload() {
+ $result = $this->client->createMultipartUpload($this->options);
+ return $result['UploadId'];
+ }
+
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Descriptions.php b/server/addons/cos/library/Qcloud/Cos/Descriptions.php
new file mode 100644
index 0000000..fa3713c
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Descriptions.php
@@ -0,0 +1,11918 @@
+ 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaTranscodeJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array('location' => 'xml', 'type' => 'string', ),
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WatermarkTemplateId' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'WatermarkTemplateId',
+ 'type' => 'string',
+ 'location' => 'xml',
+ 'sentAs' => 'WatermarkTemplateId',
+ ),
+ ),
+ 'Transcode' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Watermark' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Watermark',
+ 'type' => 'object',
+ 'sentAs' => 'Watermark',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'RemoveWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'DigitalWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Version' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IgnoreError' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaTranscodeJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Operation',
+ 'type' => 'object',
+ 'sentAs' => 'Operation',
+ 'properties' => array(
+ 'Tag' => array('location' => 'xml', 'type' => 'string', ),
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TranscodeTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ColorEnhance' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Contrast' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Correction' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Saturation' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'MsSharpen' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SharpenLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'VideoMontage' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Animation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateOnlyKeepKeyFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateTimeIntervalOfFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateFramesPerSecond' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Snapshot' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'VoiceSeparate' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AudioMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Segment' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'SDRtoHDR' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HdrMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'SuperResolution' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Resolution' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EnableScaleUp' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'WatermarkTemplateId' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'WatermarkTemplateId',
+ 'type' => 'string',
+ 'location' => 'xml',
+ 'sentAs' => 'WatermarkTemplateId',
+ ),
+ ),
+ 'Transcode' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Watermark' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Watermark',
+ 'type' => 'object',
+ 'sentAs' => 'Watermark',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'RemoveWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ 'AuObject' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ 'SpriteObject' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaJob() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}jobs/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaJobOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function DescribeMediaJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaJobs() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaJobsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Tag' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'tag',
+ ),
+ 'QueueId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'queueId',
+ ),
+ 'OrderByTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'orderByTime',
+ ),
+ 'NextToken' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'nextToken',
+ ),
+ 'Size' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'size',
+ ),
+ 'States' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'states',
+ ),
+ 'StartCreationTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'startCreationTime',
+ ),
+ 'EndCreationTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'endCreationTime',
+ ),
+ ),
+ );
+ }
+ public static function DescribeMediaJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSnapshotJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSnapshotJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SpriteObject' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Snapshot' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSnapshotJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaConcatJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaConcatJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' =>false, 'type' => 'string', 'location' => 'xml', ), // 拼接任务Input可以为空,完全用数组内的元素拼接
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'ConcatTemplate' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Index' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DirectConcat' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ConcatFragments' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'ConcatFragment',
+ 'type' => 'object',
+ 'sentAs' => 'ConcatFragment',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ // 'Mode' => array( 'type' => 'string', 'location' => 'xml', ), 拼接接口不需要Mode参数
+ ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaConcatJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DetectAudio() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}audio/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectAudioOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Object' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'DataId' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Callback' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'BizType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'CallbackVersion' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'CallbackType' => array(
+ 'type' => 'integer',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectAudioOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectAudioResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}audio/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectAudioResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectAudioResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BucketId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'ForbidState' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'AudioText' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Section' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OffsetTime' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'SpeakerResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ 'RecognitionResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'SpeakerResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ 'RecognitionResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'SpeakerResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ 'RecognitionResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'SpeakerResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ 'RecognitionResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'SpeakerResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ 'RecognitionResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LanguageResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'StartTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'EndTime' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'CosHeaders' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectTextResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}text/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectTextResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectTextResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Content' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SectionCount' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'IllegalInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'AbuseInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ 'Section' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'StartByte' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'IllegalInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AbuseInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DetectVideo() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}video/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectVideoOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BizType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackVersion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DetectContent' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'CallbackType' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Snapshot' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectVideoOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectVideoResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}video/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectVideoResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectVideoResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'DataId' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'SnapshotCount' => array('type' => 'string', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Result' => array('type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ )
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ )
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ )
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ )
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ )
+ ),
+ 'Snapshot' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'SnapshotTime' => array('type' => 'integer', 'location' => 'xml',),
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Result' => array('type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ )
+ ),
+ )
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ ),
+ )
+ ),
+ ),
+ 'AudioSection' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'OffsetTime' => array('type' => 'integer', 'location' => 'xml',),
+ 'Duration' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Result' => array('type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ ),
+ 'NonExistJobIds' => array('type' => 'string', 'location' => 'xml',)
+ ),
+ );
+ }
+
+ public static function DetectDocument() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}document/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectDocumentOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BizType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackType' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectDocumentOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectDocumentResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}document/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectDocumentResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectDocumentResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'DataId' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'PageCount' => array('type' => 'integer', 'location' => 'xml',),
+ 'Labels' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'PageSegment' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Results' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'PageNumber' => array('type' => 'integer', 'location' => 'xml',),
+ 'SheetNumber' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array('type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',)
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',)
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',)
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array('type' => 'string', 'location' => 'xml',)
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array('type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array('type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateDocProcessJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}doc_jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateDocProcessJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Tag' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'QueueId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array(
+ 'type' => 'string','required' => true,'location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string','required' => true,'location' => 'xml',
+ ),
+ 'Object' => array(
+ 'type' => 'string','required' => true,'location' => 'xml',
+ ),
+ ),
+ ),
+ 'DocProcess' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'SrcType' => array(
+ 'type' => 'string',
+ ),
+ 'TgtType' => array(
+ 'type' => 'string',
+ ),
+ 'SheetId' => array(
+ 'type' => 'integer',
+ ),
+ 'StartPage' => array(
+ 'type' => 'integer',
+ ),
+ 'EndPage' => array(
+ 'type' => 'integer',
+ ),
+ 'ImageParams' => array(
+ 'type' => 'string',
+ ),
+ 'DocPassword' => array(
+ 'type' => 'string',
+ ),
+ 'Comments' => array(
+ 'type' => 'integer',
+ ),
+ 'PaperDirection' => array(
+ 'type' => 'integer',
+ ),
+ 'Quality' => array(
+ 'type' => 'integer',
+ ),
+ 'Zoom' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateDocProcessJobsOutput()
+ {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id',),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'EndTime' => array('type' => 'string', 'location' => 'xml',),
+ 'StartTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'Tag' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'QueueId' => array('type' => 'string', 'location' => 'xml',),
+ 'Input' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ 'Object' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ ),
+ ),
+ 'DocProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'SrcType' => array(
+ 'type' => 'string',
+ ),
+ 'TgtType' => array(
+ 'type' => 'string',
+ ),
+ 'SheetId' => array(
+ 'type' => 'integer',
+ ),
+ 'StartPage' => array(
+ 'type' => 'integer',
+ ),
+ 'EndPage' => array(
+ 'type' => 'integer',
+ ),
+ 'ImageParams' => array(
+ 'type' => 'string',
+ ),
+ 'DocPassword' => array(
+ 'type' => 'string',
+ ),
+ 'Comments' => array(
+ 'type' => 'integer',
+ ),
+ 'PaperDirection' => array(
+ 'type' => 'integer',
+ ),
+ 'Quality' => array(
+ 'type' => 'integer',
+ ),
+ 'Zoom' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DescribeDocProcessQueues() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}docqueue',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeDocProcessQueuesOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'queueIds' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'state' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'pageNumber' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ ),
+ 'pageSize' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ ),
+ ),
+ );
+ }
+ public static function DescribeDocProcessQueuesOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array('type' => 'string', 'location' => 'xml',),
+ 'TotalCount' => array('type' => 'integer', 'location' => 'xml',),
+ 'PageNumber' => array('type' => 'integer', 'location' => 'xml',),
+ 'PageSize' => array('type' => 'integer', 'location' => 'xml',),
+ 'QueueList' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'Name' => array('type' => 'string', 'location' => 'xml',),
+ 'MaxSize' => array('type' => 'integer', 'location' => 'xml',),
+ 'MaxConcurrent' => array('type' => 'integer', 'location' => 'xml',),
+ 'CreateTime' => array('type' => 'string', 'location' => 'xml',),
+ 'UpdateTime' => array('type' => 'string', 'location' => 'xml',),
+ 'BucketId' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'QueueId' => array('type' => 'string', 'location' => 'xml',),
+ 'NotifyConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'Event' => array('type' => 'string', 'location' => 'xml',),
+ 'Type' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeDocProcessJob() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}doc_jobs/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeDocProcessJobOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeDocProcessJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array(
+ 'type' => 'string',
+ ),
+ 'Message' => array(
+ 'type' => 'string',
+ ),
+ 'JobId' => array(
+ 'type' => 'string',
+ ),
+ 'State' => array(
+ 'type' => 'string',
+ ),
+ 'CreationTime' => array(
+ 'type' => 'string',
+ ),
+ 'QueueId' => array(
+ 'type' => 'string',
+ ),
+ 'Tag' => array(
+ 'type' => 'string',
+ ),
+ 'EndTime' => array(
+ 'type' => 'string',
+ ),
+ 'Input' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DocProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'SrcType' => array(
+ 'type' => 'string',
+ ),
+ 'TgtType' => array(
+ 'type' => 'string',
+ ),
+ 'SheetId' => array(
+ 'type' => 'integer',
+ ),
+ 'StartPage' => array(
+ 'type' => 'integer',
+ ),
+ 'EndPage' => array(
+ 'type' => 'integer',
+ ),
+ 'ImageParams' => array(
+ 'type' => 'string',
+ ),
+ 'DocPassword' => array(
+ 'type' => 'string',
+ ),
+ 'Comments' => array(
+ 'type' => 'integer',
+ ),
+ 'PaperDirection' => array(
+ 'type' => 'integer',
+ ),
+ 'Quality' => array(
+ 'type' => 'integer',
+ ),
+ 'Zoom' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ 'DocProcessResult' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'PageInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'PageNo' => array(
+ 'type' => 'integer',
+ ),
+ 'TgtUri' => array(
+ 'type' => 'string',
+ ),
+ 'PicIndex' => array(
+ 'type' => 'integer',
+ ),
+ 'PicNum' => array(
+ 'type' => 'integer',
+ ),
+ 'X-SheetPics' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ 'SuccPageCount' => array(
+ 'type' => 'integer',
+ ),
+ 'FailPageCount' => array(
+ 'type' => 'integer',
+ ),
+ 'TaskId' => array(
+ 'type' => 'string',
+ ),
+ 'TgtType' => array(
+ 'type' => 'string',
+ ),
+ 'TotalPageCount' => array(
+ 'type' => 'integer',
+ ),
+ 'TotalSheetCount' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Bucket' => array(
+ 'type' => 'string',
+ ),
+ 'Object' => array(
+ 'type' => 'string',
+ ),
+ 'Region' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDescribeDocProcessJobs() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}doc_jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDescribeDocProcessJobsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Tag' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'tag',
+ ),
+ 'QueueId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'queueId',
+ ),
+ 'OrderByTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'orderByTime',
+ ),
+ 'NextToken' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'nextToken',
+ ),
+ 'Size' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'size',
+ ),
+ 'States' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'states',
+ ),
+ 'StartCreationTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'startCreationTime',
+ ),
+ 'EndCreationTime' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'endCreationTime',
+ ),
+ ),
+ );
+ }
+
+ public static function GetDescribeDocProcessJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id',),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'NextToken' => array('type' => 'string','location' => 'xml',),
+ 'JobsDetail' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'Tag' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'QueueId' => array('type' => 'string', 'location' => 'xml',),
+ 'Input' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ 'Object' => array(
+ 'type' => 'string','location' => 'xml',
+ ),
+ ),
+ ),
+ 'DocProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'SrcType' => array(
+ 'type' => 'string',
+ ),
+ 'TgtType' => array(
+ 'type' => 'string',
+ ),
+ 'SheetId' => array(
+ 'type' => 'integer',
+ ),
+ 'StartPage' => array(
+ 'type' => 'integer',
+ ),
+ 'EndPage' => array(
+ 'type' => 'integer',
+ ),
+ 'ImageParams' => array(
+ 'type' => 'string',
+ ),
+ 'DocPassword' => array(
+ 'type' => 'string',
+ ),
+ 'Comments' => array(
+ 'type' => 'integer',
+ ),
+ 'PaperDirection' => array(
+ 'type' => 'integer',
+ ),
+ 'Quality' => array(
+ 'type' => 'integer',
+ ),
+ 'Zoom' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DetectImage() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectImageOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ ),
+ 'DetectType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'detect-type'
+ ),
+ 'DetectUrl' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'detect-url'
+ ),
+ 'Interval' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'interval'
+ ),
+ 'MaxFrames' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'max-frames'
+ ),
+ 'BizType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'biz-type'
+ ),
+ 'LargeImageDetect' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'large-image-detect'
+ ),
+ 'DataId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'dataid'
+ ),
+ 'Async' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'async'
+ ),
+ 'Callback' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'callback'
+ ),
+ ),
+ );
+ }
+
+ public static function DetectImageOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'Result' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'CompressionResult' => array('type' => 'integer', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'DataId' => array('type' => 'string', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerroristInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function DetectImages() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}image/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectImagesOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Inputs' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Input',
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'sentAs' => 'Input',
+ 'properties' => array(
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Content' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Interval' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'MaxFrames' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LargeImageDetect' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BizType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Async' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DetectImagesOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'JobsDetail' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Message' => array( 'type' => 'string', 'location' => 'xml',),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CompressionResult' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Msg' => array( 'type' => 'string', 'location' => 'xml',),
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function DetectVirus() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}virus/detect',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectVirusOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectVirusOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectVirusResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}virus/detect/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectVirusResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectVirusResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array('type' => 'string', 'location' => 'xml',),
+ 'DetectDetail' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Result' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FileName' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VirusName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectImageResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}image/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectImageResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectImageResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Object' => array('type' => 'string', 'location' => 'xml',),
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'CompressionResult' => array('type' => 'integer', 'location' => 'xml',),
+ 'Text' => array('type' => 'string', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'Result' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TeenagerInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Category' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVoiceSeparateJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVoiceSeparateJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VoiceSeparate' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AudioMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array('type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array('type' => 'string', 'location' => 'xml', ),
+ 'Object' => array('type' => 'string', 'location' => 'xml', ),
+ 'AuObject' => array('type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVoiceSeparateJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaVoiceSeparateJob() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}jobs/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaVoiceSeparateJobOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function DescribeMediaVoiceSeparateJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DetectWebpage() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}webpage/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectWebpageOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReturnHighlightHtml' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackType' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectWebpageOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetDetectWebpageResult() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}webpage/auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetDetectWebpageResultOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetDetectWebpageResultOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array('type' => 'string', 'location' => 'xml',),
+ 'Message' => array('type' => 'string', 'location' => 'xml',),
+ 'DataId' => array('type' => 'string', 'location' => 'xml',),
+ 'JobId' => array('type' => 'string', 'location' => 'xml',),
+ 'State' => array('type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array('type' => 'string', 'location' => 'xml',),
+ 'Url' => array('type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array('type' => 'integer', 'location' => 'xml',),
+ 'Label' => array('type' => 'string', 'location' => 'xml',),
+ 'PageCount' => array('type' => 'integer', 'location' => 'xml',),
+ 'HighlightHtml' => array('type' => 'string', 'location' => 'xml',),
+ 'Labels' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ 'ImageResults' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Results' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Category' => array('type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array('type' => 'string', 'location' => 'xml',),
+ 'OcrResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ObjectResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Location' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'numeric', 'location' => 'xml',),
+ 'Rotate' => array( 'type' => 'numeric', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ImageId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TextResults' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Results' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Text' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Suggestion' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array('type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array('type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array('type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array('type' => 'integer', 'location' => 'xml',),
+ 'Score' => array('type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array('type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaBuckets() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/mediabucket',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaBucketsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Regions' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'regions' ),
+ 'BucketNames' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'bucketNames' ),
+ 'BucketName' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'bucketName' ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageNumber' ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageSize' ),
+ ),
+ );
+ }
+ public static function DescribeMediaBucketsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'TotalCount' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageNumber' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageSize' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'MediaBucketList' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'BucketId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Region' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetPrivateM3U8() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetPrivateM3U8Output',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ ),
+ 'expires' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ )
+ ),
+ );
+ }
+ public static function GetPrivateM3U8Output() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaQueues() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}queue',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaQueuesOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'QueueIds' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'queueIds' ),
+ 'State' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'state' ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageNumber' ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageSize' ),
+ ),
+ );
+ }
+ public static function DescribeMediaQueuesOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'TotalCount' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageNumber' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageSize' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'QueueList' => array(
+ 'type' => 'array', 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object', 'location' => 'xml',
+ 'properties' => array(
+ 'QueueId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'State' => array( 'type' => 'string', 'location' => 'xml',),
+ 'MaxSize' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'MaxConcurrent' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'UpdateTime' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml',),
+ 'NotifyConfig' => array(
+ 'type' => 'object', 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml',),
+ 'State' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Event' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'NonExistPIDs' => array(
+ 'type' => 'array', 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaQueue() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}queue/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaQueueOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueID' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'State' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'NotifyConfig' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ 'Event' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'required' => false, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaQueueOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSmartCoverJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSmartCoverJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SmartCover' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteDuplicates' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaSmartCoverJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVideoProcessJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVideoProcessJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TranscodeTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WatermarkTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ColorEnhance' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Contrast' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Correction' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Saturation' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'MsSharpen' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SharpenLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Transcode' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Watermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'DigitalWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Version' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IgnoreError' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVideoProcessJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVideoMontageJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVideoMontageJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoMontage' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Rotate' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'AudioMix' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AudioSource' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MixMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Replace' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EffectConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'EnableStartFadein' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartFadeinTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EnableEndFadeout' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndFadeoutTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EnableBgmFade' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BgmFadeTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVideoMontageJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaAnimationJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaAnimationJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'required' => false, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Animation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateOnlyKeepKeyFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateTimeIntervalOfFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateFramesPerSecond' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaAnimationJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaPicProcessJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}pic_jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaPicProcessJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PicProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsPicInfo' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ProcessRule' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaPicProcessJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSegmentJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSegmentJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Segment' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaSegmentJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVideoTagJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVideoTagJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoTag' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Scenario' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVideoTagJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSuperResolutionJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSuperResolutionJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TranscodeTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WatermarkTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SuperResolution' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Resolution' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EnableScaleUp' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Transcode' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Watermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'DigitalWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Version' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IgnoreError' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaSuperResolutionJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSDRtoHDRJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSDRtoHDRJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TranscodeTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WatermarkTemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SDRtoHDR' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HdrMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Transcode' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Watermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaSDRtoHDRJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaDigitalWatermarkJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaDigitalWatermarkJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DigitalWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Message' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Version' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaDigitalWatermarkJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaExtractDigitalWatermarkJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaExtractDigitalWatermarkJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ExtractDigitalWatermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Version' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaExtractDigitalWatermarkJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DetectLiveVideo() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}video/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectLiveVideoOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BizType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackType' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function DetectLiveVideoOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CancelLiveVideoAuditing() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}video/cancel_auditing/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CancelLiveVideoAuditingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function CancelLiveVideoAuditingOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function OpticalOcrRecognition() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'OpticalOcrRecognitionOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'CiProcess' => array( 'required' => true, 'type' => 'string', 'location' => 'query', 'sentAs' => 'ci-process' ),
+ 'Type' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'type' ),
+ 'LanguageType' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'language-type' ),
+ 'IsPDF' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'ispdf' ),
+ 'PdfPageNumber' => array( 'type' => 'integer', 'location' => 'query', 'sentAs' => 'pdf-pagenumber' ),
+ 'IsWord' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'isword' ),
+ 'EnableWordPolygon' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'enable-word-polygon' ),
+ ),
+ );
+ }
+ public static function OpticalOcrRecognitionOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function TriggerWorkflow() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}triggerworkflow',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'TriggerWorkflowOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'workflowId' => array( 'required' => true, 'type' => 'string', 'location' => 'query', ),
+ 'object' => array( 'required' => true, 'type' => 'string', 'location' => 'query', ),
+ 'name' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ ),
+ );
+ }
+ public static function TriggerWorkflowOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'InstanceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ );
+ }
+
+ public static function GetWorkflowInstances() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}workflowexecution',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetWorkflowInstancesOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'workflowId' => array( 'required' => true, 'type' => 'string', 'location' => 'query', ),
+ 'name' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'orderByTime' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'size' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'states' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'startCreationTime' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'endCreationTime' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ 'nextToken' => array( 'required' => false, 'type' => 'string', 'location' => 'query', ),
+ ),
+ );
+ }
+ public static function GetWorkflowInstancesOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'NextToken' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WorkflowExecutionList' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'RunId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'WorkflowId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function GetWorkflowInstance() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}workflowexecution/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetWorkflowInstanceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function GetWorkflowInstanceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSnapshotTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSnapshotTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Snapshot' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaSnapshotTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'Template' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UpdateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Snapshot' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaSnapshotTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaSnapshotTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Snapshot' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaSnapshotTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'Template' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tag' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TemplateId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UpdateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Snapshot' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Count' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaTranscodeTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaTranscodeTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ClipConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Rotate' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaTranscodeTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaTranscodeTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaTranscodeTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ClipConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LongShortMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Rotate' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'KeepTwoTracks' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SwitchTrack' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SampleFormat' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AdjDarMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckVideoBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'VideoBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsCheckAudioBitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AudioBitrateAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeleteMetadata' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsEncrypt' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsHlsEncrypt' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UriKey' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaTranscodeTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaHighSpeedHdTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaHighSpeedHdTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaHighSpeedHdTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaHighSpeedHdTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaHighSpeedHdTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Profile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gop' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Preset' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bufsize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Maxrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'HlsTsTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pixfmt' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TransConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsCheckReso' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ResoAdjMethod' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IsHdr2Sdr' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaHighSpeedHdTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaAnimationTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaAnimationTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateOnlyKeepKeyFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateTimeIntervalOfFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateFramesPerSecond' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaAnimationTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaAnimationTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaAnimationTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateOnlyKeepKeyFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateTimeIntervalOfFrame' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AnimateFramesPerSecond' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Duration' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaAnimationTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaConcatTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaConcatTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'ConcatTemplate' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ConcatFragments' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'ConcatFragment',
+ 'type' => 'object',
+ 'sentAs' => 'ConcatFragment',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaConcatTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaConcatTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaConcatTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'ConcatTemplate' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ConcatFragments' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'ConcatFragment',
+ 'type' => 'object',
+ 'sentAs' => 'ConcatFragment',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaConcatTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVideoProcessTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVideoProcessTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'ColorEnhance' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Contrast' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Correction' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Saturation' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'MsSharpen' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SharpenLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVideoProcessTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaVideoProcessTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaVideoProcessTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'ColorEnhance' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Contrast' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Correction' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Saturation' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'MsSharpen' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Enable' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SharpenLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaVideoProcessTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVideoMontageTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVideoMontageTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Duration' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'AudioMix' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AudioSource' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MixMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Replace' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVideoMontageTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaVideoMontageTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaVideoMontageTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Duration' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Container' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Fps' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Crf' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Remove' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'AudioMix' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'AudioSource' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MixMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Replace' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaVideoMontageTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaVoiceSeparateTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaVoiceSeparateTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'AudioMode' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'AudioConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaVoiceSeparateTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaVoiceSeparateTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaVoiceSeparateTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'AudioMode' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'AudioConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Codec' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Samplerate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bitrate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Channels' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaVoiceSeparateTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaSuperResolutionTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaSuperResolutionTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Resolution' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'EnableScaleUp' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Version' => array( 'location' => 'xml', 'type' => 'string', ),
+ ),
+ );
+ }
+ public static function CreateMediaSuperResolutionTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaSuperResolutionTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaSuperResolutionTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Resolution' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'EnableScaleUp' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Version' => array( 'location' => 'xml', 'type' => 'string', ),
+ ),
+ );
+ }
+ public static function UpdateMediaSuperResolutionTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaPicProcessTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaPicProcessTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'PicProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsPicInfo' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ProcessRule' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaPicProcessTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaPicProcessTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaPicProcessTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'PicProcess' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IsPicInfo' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ProcessRule' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaPicProcessTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaWatermarkTemplate() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaWatermarkTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Watermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaWatermarkTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateMediaWatermarkTemplate() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}template/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateMediaWatermarkTemplateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Name' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Watermark' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Pos' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'LocMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dx' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Dy' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StartTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'EndTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Image' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Mode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Width' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Height' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Background' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Text' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FontSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'FontColor' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Transparency' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Text' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateMediaWatermarkTemplateOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeMediaTemplates() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}template',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeMediaTemplatesOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Tag' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'tag' ),
+ 'Category' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'category' ),
+ 'Ids' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'ids' ),
+ 'Name' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'name' ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageNumber' ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageSize' ),
+ ),
+ );
+ }
+ public static function DescribeMediaTemplatesOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'TotalCount' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TemplateList' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeWorkflow() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}workflow',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeWorkflowOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Ids' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'ids' ),
+ 'Name' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'name' ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageNumber' ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageSize' ),
+ ),
+ );
+ }
+ public static function DescribeWorkflowOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'TotalCount' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MediaWorkflowList' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function DeleteWorkflow() {
+ return array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}workflow/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteWorkflowOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function DeleteWorkflowOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'WorkflowId' => array( 'type' => 'string', 'location' => 'xml' ),
+ ),
+ );
+ }
+
+ public static function CreateInventoryTriggerJob() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}inventorytriggerjob',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateInventoryTriggerJobOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Name' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Manifest' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UrlFile' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Prefix' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'WorkflowIds' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'TimeInterval' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Start' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'End' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateInventoryTriggerJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeInventoryTriggerJobs() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}inventorytriggerjob',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeInventoryTriggerJobsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'NextToken' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'nextToken' ),
+ 'Size' => array( 'type' => 'integer', 'location' => 'query', 'sentAs' => 'size' ),
+ 'OrderByTime' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'orderByTime' ),
+ 'States' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'states' ),
+ 'StartCreationTime' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'startCreationTime' ),
+ 'EndCreationTime' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'endCreationTime' ),
+ 'WorkflowId' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'workflowId' ),
+ 'JobId' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'jobId' ),
+ 'Name' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'name' ),
+ ),
+ );
+ }
+ public static function DescribeInventoryTriggerJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function DescribeInventoryTriggerJob() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}inventorytriggerjob/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeInventoryTriggerJobOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function DescribeInventoryTriggerJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CancelInventoryTriggerJob() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}inventorytriggerjob/{/Key*}?cancel',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CancelInventoryTriggerJobOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function CancelInventoryTriggerJobOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaNoiseReductionJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaNoiseReductionJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaNoiseReductionJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function ImageRepairProcess() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageRepairProcessOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ ),
+ 'MaskPic' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ ),
+ 'MaskPoly' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ ),
+ ),
+ );
+ }
+ public static function ImageRepairProcessOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function ImageDetectCarProcess() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=DetectCar',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageDetectCarProcessOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function ImageDetectCarProcessOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'CarTags' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Serial' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Brand' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Color' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Confidence' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ 'Year' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ 'CarLocation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ ),
+ ),
+ 'PlateContent' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Plate' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Color' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PlateLocation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ 'Y' => array( 'type' => 'numeric', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function ImageAssessQualityProcess() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=AssessQuality',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageAssessQualityProcessOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ );
+ }
+ public static function ImageAssessQualityProcessOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'LongImage' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BlackAndWhite' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'SmallImage' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BigImage' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'PureImage' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ClarityScore' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AestheticScore' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ );
+ }
+
+ public static function ImageSearchOpen() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}ImageSearchBucket',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageSearchOpenOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'MaxCapacity' => array( 'location' => 'xml', 'type' => 'integer', ),
+ 'MaxQps' => array( 'location' => 'xml', 'type' => 'integer', ),
+ ),
+ );
+ }
+ public static function ImageSearchOpenOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function ImageSearchAdd() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=ImageSearch&action=AddImage',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageSearchAddOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'EntityId' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CustomContent' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Tags' => array( 'location' => 'xml', 'type' => 'string', ),
+ ),
+ );
+ }
+ public static function ImageSearchAddOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function ImageSearch() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=ImageSearch&action=SearchImage',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageSearchOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'MatchThreshold' => array( 'type' => 'integer', 'location' => 'query' ),
+ 'Offset' => array( 'type' => 'integer', 'location' => 'query' ),
+ 'Limit' => array( 'type' => 'integer', 'location' => 'query' ),
+ 'Filter' => array( 'type' => 'string', 'location' => 'query' ),
+ ),
+ );
+ }
+ public static function ImageSearchOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'Count' => array('type' => 'integer', 'location' => 'xml',),
+ 'ImageInfos' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'EntityId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CustomContent' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Tags' => array( 'type' => 'string', 'location' => 'xml',),
+ 'PicName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function ImageSearchDelete() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=ImageSearch&action=DeleteImage',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageSearchDeleteOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'EntityId' => array( 'location' => 'xml', 'type' => 'string', ),
+ ),
+ );
+ }
+ public static function ImageSearchDeleteOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function BindCiService() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'BindCiServiceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function BindCiServiceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function GetCiService() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetCiServiceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function GetCiServiceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function UnBindCiService() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?unbind',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UnBindCiServiceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function UnBindCiServiceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function GetHotLink() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?hotlink',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetHotLinkOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function GetHotLinkOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function AddHotLink() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?hotlink',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'AddHotLinkOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Hotlink',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Type' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Urls' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array( 'name' => 'Url', 'type' => 'string', 'location' => 'xml', 'sentAs' => 'Url', ),
+ ),
+ ),
+ );
+ }
+ public static function AddHotLinkOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function OpenOriginProtect() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?origin-protect',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'OpenOriginProtectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function OpenOriginProtectOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function GetOriginProtect() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?origin-protect',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetOriginProtectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function GetOriginProtectOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CloseOriginProtect() {
+ return array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?origin-protect',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CloseOriginProtectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function CloseOriginProtectOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function ImageDetectFace() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=DetectFace',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageDetectFaceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'MaxFaceNum' => array( 'type' => 'integer', 'location' => 'query', 'sentAs' => 'max-face-num' ),
+ ),
+ );
+ }
+ public static function ImageDetectFaceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'ImageWidth' => array('type' => 'integer', 'location' => 'xml',),
+ 'ImageHeight' => array('type' => 'integer', 'location' => 'xml',),
+ 'FaceModelVersion' => array('type' => 'string', 'location' => 'xml',),
+ 'FaceInfos' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'X' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Y' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Width' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Height' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function ImageFaceEffect() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=face-effect',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageFaceEffectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'type' => array( 'type' => 'string', 'location' => 'query', ),
+ 'whitening' => array( 'type' => 'integer', 'location' => 'query', ),
+ 'smoothing' => array( 'type' => 'integer', 'location' => 'query', ),
+ 'faceLifting' => array( 'type' => 'integer', 'location' => 'query', ),
+ 'eyeEnlarging' => array( 'type' => 'integer', 'location' => 'query', ),
+ 'gender' => array( 'type' => 'integer', 'location' => 'query', ),
+ 'age' => array( 'type' => 'integer', 'location' => 'query', ),
+ ),
+ );
+ }
+ public static function ImageFaceEffectOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'ResultImage' => array('type' => 'string', 'location' => 'xml',),
+ 'ResultMask' => array('type' => 'string', 'location' => 'xml',),
+ )
+ );
+ }
+
+ public static function IDCardOCR() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=IDCardOCR',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'IDCardOCROutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'CardSide' => array( 'type' => 'string', 'location' => 'query', ),
+ 'Config' => array( 'type' => 'string', 'location' => 'query', ),
+ ),
+ );
+ }
+ public static function IDCardOCROutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'ResultImage' => array('type' => 'string', 'location' => 'xml',),
+ 'IdInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Sex' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nation' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Birth' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Address' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IdNum' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Authority' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ValidDate' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'AdvancedInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IdCard' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Portrait' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml',),
+ 'BorderCodeValue' => array( 'type' => 'string', 'location' => 'xml',),
+ 'WarnInfos' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function IDCardOCRByUpload() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=IDCardOCR',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'IDCardOCRByUploadOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'CardSide' => array( 'type' => 'string', 'location' => 'query', ),
+ 'Config' => array( 'type' => 'string', 'location' => 'query', ),
+ 'Body' => array(
+ 'required' => true,
+ 'type' => array( 'any' ),
+ 'location' => 'body'
+ ),
+ ),
+ );
+ }
+ public static function IDCardOCRByUploadOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'ResultImage' => array('type' => 'string', 'location' => 'xml',),
+ 'IdInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Sex' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nation' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Birth' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Address' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IdNum' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Authority' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ValidDate' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'AdvancedInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'IdCard' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Portrait' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Quality' => array( 'type' => 'string', 'location' => 'xml',),
+ 'BorderCodeValue' => array( 'type' => 'string', 'location' => 'xml',),
+ 'WarnInfos' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ )
+ );
+ }
+
+ public static function GetLiveCode() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?ci-process=GetLiveCode',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetLiveCodeOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function GetLiveCodeOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'LiveCode' => array('type' => 'string', 'location' => 'xml',),
+ )
+ );
+ }
+
+ public static function GetActionSequence() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?ci-process=GetActionSequence',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetActionSequenceOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ ),
+ );
+ }
+ public static function GetActionSequenceOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'location' => 'header', 'sentAs' => 'x-cos-request-id', ),
+ 'ContentType' => array('type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type',),
+ 'ContentLength' => array('type' => 'numeric', 'minimum' => 0, 'location' => 'header', 'sentAs' => 'Content-Length',),
+ 'ActionSequence' => array('type' => 'string', 'location' => 'xml',),
+ )
+ );
+ }
+
+ public static function DescribeDocProcessBuckets() {
+ return array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/docbucket',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DescribeDocProcessBucketsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Regions' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'regions' ),
+ 'BucketNames' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'bucketNames' ),
+ 'BucketName' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'bucketName' ),
+ 'PageNumber' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageNumber' ),
+ 'PageSize' => array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'pageSize' ),
+ ),
+ );
+ }
+ public static function DescribeDocProcessBucketsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'TotalCount' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageNumber' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'PageSize' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'DocBucketList' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'BucketId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Region' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AliasBucketId' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function UpdateDocProcessQueue() {
+ return array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}docqueue/{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UpdateDocProcessQueueOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Key' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'QueueID' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'NotifyConfig' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Event' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function UpdateDocProcessQueueOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'x-ci-request-id', ),
+ 'ContentType' => array( 'type' => 'string', 'location' => 'header', 'sentAs' => 'Content-Type', ),
+ 'ContentLength' => array( 'type' => 'numeric', 'minimum'=> 0, 'location' => 'header', 'sentAs' => 'Content-Length', ),
+ 'Queue' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'QueueId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Name' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CreateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'UpdateTime' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MaxSize' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'MaxConcurrent' => array( 'type' => 'integer', 'location' => 'xml', ),
+ 'NotifyConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Url' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'State' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Event' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaQualityEstimateJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaQualityEstimateJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaQualityEstimateJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+ public static function CreateMediaStreamExtractJobs() {
+ return array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}jobs',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMediaStreamExtractJobsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array( 'required' => true, 'type' => 'string', 'location' => 'uri', ),
+ 'Tag' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'QueueId' => array( 'required' => true, 'location' => 'xml', 'type' => 'string', ),
+ 'CallBack' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackFormat' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'CallBackType' => array( 'location' => 'xml', 'type' => 'string', ),
+ 'Input' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array( 'required' => true, 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ 'Operation' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'UserData' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'JobLevel' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Output' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Bucket' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Region' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'StreamExtracts' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'StreamExtract',
+ 'type' => 'object',
+ 'sentAs' => 'StreamExtract',
+ 'properties' => array(
+ 'Index' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Object' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'CallBackMqConfig' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'MqRegion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqMode' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'MqName' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ );
+ }
+ public static function CreateMediaStreamExtractJobsOutput() {
+ return array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ );
+ }
+
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Exception/CosException.php b/server/addons/cos/library/Qcloud/Cos/Exception/CosException.php
new file mode 100644
index 0000000..8e482c6
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Exception/CosException.php
@@ -0,0 +1,5 @@
+exceptionCode = $code;
+ }
+
+ /**
+ * Get the exception code
+ *
+ * @return string|null
+ */
+ public function getExceptionCode() {
+ return $this->exceptionCode;
+ }
+
+ /**
+ * Set the exception type
+ *
+ * @param string $type Exception type
+ */
+ public function setExceptionType($type) {
+ $this->exceptionType = $type;
+ }
+
+ /**
+ * Get the exception type (one of client or server)
+ *
+ * @return string|null
+ */
+ public function getExceptionType() {
+ return $this->exceptionType;
+ }
+
+ /**
+ * Set the request ID
+ *
+ * @param string $id Request ID
+ */
+ public function setRequestId($id) {
+ $this->requestId = $id;
+ }
+
+ /**
+ * Get the Request ID
+ *
+ * @return string|null
+ */
+ public function getRequestId() {
+ return $this->requestId;
+ }
+
+ /**
+ * Set the associated response
+ *
+ * @param Response $response
+ */
+ public function setResponse(ResponseInterface $response) {
+ $this->response = $response;
+ }
+
+ /**
+ * Get the associated response object
+ *
+ * @return Response|null
+ */
+ public function getResponse() {
+ return $this->response;
+ }
+
+ /**
+ * Set the associated request
+ *
+ * @param RequestInterface $request
+ */
+ public function setRequest(RequestInterface $request) {
+ $this->request = $request;
+ }
+
+ /**
+ * Get the associated request object
+ *
+ * @return RequestInterface|null
+ */
+ public function getRequest() {
+ return $this->request;
+ }
+
+ /**
+ * Get the status code of the response
+ *
+ * @return int|null
+ */
+ public function getStatusCode() {
+ return $this->response ? $this->response->getStatusCode() : null;
+ }
+
+ /**
+ * Cast to a string
+ *
+ * @return string
+ */
+ public function __toString() {
+ $message = get_class($this) . ': '
+ . 'Cos Error Code: ' . $this->getExceptionCode() . ', '
+ . 'Status Code: ' . $this->getStatusCode() . ', '
+ . 'Cos Request ID: ' . $this->getRequestId() . ', '
+ . 'Cos Error Type: ' . $this->getExceptionType() . ', '
+ . 'Cos Error Message: ' . $this->getMessage();
+
+ // Add the User-Agent if available
+ if ($this->request) {
+ $message .= ', ' . 'User-Agent: ' . $this->request->getHeader('User-Agent')[0];
+ }
+
+ return $message;
+ }
+
+ /**
+ * Get the request ID of the error. This value is only present if a
+ * response was received, and is not present in the event of a networking
+ * error.
+ *
+ * Same as `getRequestId()` method, but matches the interface for SDKv3.
+ *
+ * @return string|null Returns null if no response was received
+ */
+ public function getCosRequestId() {
+ return $this->requestId;
+ }
+
+ /**
+ * Get the Cos error type.
+ *
+ * Same as `getExceptionType()` method, but matches the interface for SDKv3.
+ *
+ * @return string|null Returns null if no response was received
+ */
+ public function getCosErrorType() {
+ return $this->exceptionType;
+ }
+
+ /**
+ * Get the Cos error code.
+ *
+ * Same as `getExceptionCode()` method, but matches the interface for SDKv3.
+ *
+ * @return string|null Returns null if no response was received
+ */
+ public function getCosErrorCode() {
+ return $this->exceptionCode;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ExceptionMiddleware.php b/server/addons/cos/library/Qcloud/Cos/ExceptionMiddleware.php
new file mode 100644
index 0000000..3bc980a
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ExceptionMiddleware.php
@@ -0,0 +1,71 @@
+nextHandler = $nextHandler;
+ $this->parser = new ExceptionParser();
+ $this->defaultException = 'Qcloud\Cos\Exception\ServiceResponseException';
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return PromiseInterface
+ */
+ public function __invoke(RequestInterface $request, array $options) {
+ $fn = $this->nextHandler;
+ return $fn($request, $options)->then(
+ function (ResponseInterface $response) use ($request) {
+ return $this->handle($request, $response);
+ }
+ );
+ }
+
+ public function handle(RequestInterface $request, ResponseInterface $response) {
+ $code = $response->getStatusCode();
+ if ($code < 400) {
+ return $response;
+ }
+
+ //throw RequestException::create($request, $response);
+ $parts = $this->parser->parse($request, $response);
+
+ $className = 'Qcloud\\Cos\\Exception\\' . $parts['code'];
+ if (substr($className, -9) !== 'Exception') {
+ $className .= 'Exception';
+ }
+
+ $className = class_exists($className) ? $className : $this->defaultException;
+
+ throw $this->createException($className, $request, $response, $parts);
+ }
+
+ protected function createException($className, RequestInterface $request, ResponseInterface $response, array $parts) {
+ $class = new $className($parts['message']);
+
+ if ($class instanceof ServiceResponseException) {
+ $class->setExceptionCode($parts['code']);
+ $class->setExceptionType($parts['type']);
+ $class->setResponse($response);
+ $class->setRequest($request);
+ $class->setRequestId($parts['request_id']);
+ }
+ return $class;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ExceptionParser.php b/server/addons/cos/library/Qcloud/Cos/ExceptionParser.php
new file mode 100644
index 0000000..c15400b
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ExceptionParser.php
@@ -0,0 +1,112 @@
+ null,
+ 'message' => null,
+ //'type' => $response->isClientError() ? 'client' : 'server',
+ 'type' => 'client',
+ 'request_id' => null,
+ 'parsed' => null
+ );
+
+ $body = strval($response->getBody());
+
+ if (empty($body)) {
+ $this->parseHeaders($request, $response, $data);
+ return $data;
+ }
+
+ try {
+ $xml = new \SimpleXMLElement(utf8_encode($body));
+ $this->parseBody($xml, $data);
+ return $data;
+ } catch (\Exception $e) {
+ $data['code'] = 'PhpInternalXmlParseError';
+ $data['message'] = 'A non-XML response was received';
+ return $data;
+ }
+ }
+
+ /**
+ * Parses additional exception information from the response headers
+ *
+ * @param RequestInterface $request Request that was issued
+ * @param Response $response The response from the request
+ * @param array $data The current set of exception data
+ */
+ protected function parseHeaders(RequestInterface $request, ResponseInterface $response, array &$data) {
+ $data['message'] = $response->getStatusCode() . ' ' . $response->getReasonPhrase();
+ $requestId = $response->getHeader('x-cos-request-id');
+ if (isset($requestId[0])) {
+ $requestId = $requestId[0];
+ $data['request_id'] = $requestId;
+ $data['message'] .= " (Request-ID: $requestId)";
+ }
+
+ // Get the request
+ $status = $response->getStatusCode();
+ $method = $request->getMethod();
+
+ // Attempt to determine code for 403s and 404s
+ if ($status === 403) {
+ $data['code'] = 'AccessDenied';
+ } elseif ($method === 'HEAD' && $status === 404) {
+ $path = explode('/', trim($request->getUri()->getPath(), '/'));
+ $host = explode('.', $request->getUri()->getHost());
+ $bucket = (count($host) >= 4) ? $host[0] : array_shift($path);
+ $object = array_shift($path);
+
+ if ($bucket && $object) {
+ $data['code'] = 'NoSuchKey';
+ } elseif ($bucket) {
+ $data['code'] = 'NoSuchBucket';
+ }
+ }
+ }
+
+ /**
+ * Parses additional exception information from the response body
+ *
+ * @param \SimpleXMLElement $body The response body as XML
+ * @param array $data The current set of exception data
+ */
+ protected function parseBody(\SimpleXMLElement $body, array &$data) {
+ $data['parsed'] = $body;
+
+ $namespaces = $body->getDocNamespaces();
+ if (isset($namespaces[''])) {
+ // Account for the default namespace being defined and PHP not being able to handle it :(
+ $body->registerXPathNamespace('ns', $namespaces['']);
+ $prefix = 'ns:';
+ } else {
+ $prefix = '';
+ }
+
+ if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
+ $data['code'] = (string) $tempXml[0];
+ }
+
+ if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
+ $data['message'] = (string) $tempXml[0];
+ }
+
+ $tempXml = $body->xpath("//{$prefix}RequestId[1]");
+ if (empty($tempXml)) {
+ $tempXml = $body->xpath("//{$prefix}RequestID[1]");
+ }
+ if (isset($tempXml[0])) {
+ $data['request_id'] = (string) $tempXml[0];
+ }
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/BlindWatermarkTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/BlindWatermarkTemplate.php
new file mode 100644
index 0000000..0f03b1e
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/BlindWatermarkTemplate.php
@@ -0,0 +1,87 @@
+markType = 3;
+ $this->type = "";
+ $this->image = "";
+ $this->text = "";
+ $this->level = "";
+
+ }
+
+ public function setPick() {
+ $this->markType = 4;
+ }
+
+ public function setType($value) {
+ $this->type = "/type/" . $value;
+ }
+
+ public function setImage($value) {
+ $this->image = "/image/" . $this->ciBase64($value);
+ }
+
+ public function setText($value) {
+ $this->text = "/text/" . $this->ciBase64($value);
+ }
+
+ public function setLevel($value) {
+ $this->level = "/level/" . $value;
+ }
+
+ public function getType() {
+ return $this->type;
+ }
+
+ public function getImage() {
+ return $this->image;
+ }
+
+ public function getText() {
+ return $this->text;
+ }
+
+ public function getLevel() {
+ return $this->level;
+ }
+
+
+ public function queryString() {
+ $head = "watermark/$this->markType";
+ $res = "";
+ if($this->type){
+ $res .= $this->type;
+ }
+ if($this->image){
+ $res .= $this->image;
+ }
+ if($this->text){
+ $res .= $this->text;
+ }
+ if($this->level){
+ $res .= $this->level;
+ }
+ if($res){
+ $res = $head . $res;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->markType = 3;
+ $this->type = "";
+ $this->image = "";
+ $this->text = "";
+ $this->level = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIParamTransformation.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIParamTransformation.php
new file mode 100644
index 0000000..0ee780a
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIParamTransformation.php
@@ -0,0 +1,39 @@
+spilt = $spilt;
+ $this->tranParams = array();
+ $this->tranString = "";
+ }
+
+ public function addRule(ImageTemplate $template) {
+ if($template->queryString()){
+ $this->tranParams[] = $template->queryString();
+ }
+ }
+
+ public function queryString() {
+ if($this->tranParams) {
+ $this->tranString = implode($this->spilt, $this->tranParams);
+ }
+ return $this->tranString;
+ }
+
+ public function resetRule() {
+ $this->tranParams = array();
+ $this->tranString = "";
+ }
+
+ public function defineRule($value) {
+ $this->tranParams[] = $value;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIProcessTransformation.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIProcessTransformation.php
new file mode 100644
index 0000000..8988909
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/CIProcessTransformation.php
@@ -0,0 +1,31 @@
+tranParams['ci-process'] = $ciProcess;
+ $this->tranString = "";
+ }
+
+ public function addParam($name, $value, $base64 = false) {
+ if(is_string($name) && strlen($name) > 0){
+ if($base64) {
+ $value = $this->ciBase64($value);
+ }
+ $this->tranParams[$name] = $value;
+ }
+ }
+
+ public function queryString() {
+ if($this->tranParams) {
+ $this->tranString = http_build_query($this->tranParams);
+ }
+ return $this->tranString;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageMogrTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageMogrTemplate.php
new file mode 100644
index 0000000..0f8c24f
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageMogrTemplate.php
@@ -0,0 +1,374 @@
+tranParams = array();
+ $this->tranString = "";
+ }
+
+ /**
+ * 指定图片的宽高为原图的 Scale%
+ * @param $widthScale
+ */
+ public function thumbnailByScale($widthScale) {
+ $this->tranParams[] = "/thumbnail/!" . $widthScale . "p";
+ }
+
+ /**
+ * 指定图片的宽为原图的 Scale%,高度不变
+ * @param $heightScale
+ */
+ public function thumbnailByWidthScale($heightScale) {
+ $this->tranParams[] = "/thumbnail/!" . $heightScale . "px";
+ }
+
+ /**
+ * 指定图片的高为原图的 Scale%,宽度不变
+ * @param $scale
+ */
+ public function thumbnailByHeightScale($scale) {
+ $this->tranParams[] = "/thumbnail/!x" . $scale . "p";
+ }
+
+ /**
+ * 指定目标图片宽度为 Width,高度等比缩放
+ * @param $width
+ */
+ public function thumbnailByWidth($width) {
+ $this->tranParams[] = "/thumbnail/" . $width . "x";
+ }
+
+ /**
+ * 指定目标图片高度为 Height,宽度等比缩放
+ * @param $height
+ */
+ public function thumbnailByHeight($height) {
+ $this->tranParams[] = "/thumbnail/x" . $height;
+ }
+
+ /**
+ * 限定缩略图的宽度和高度的最大值分别为 Width 和 Height,进行等比缩放
+ * @param $maxW
+ * @param $maxH
+ */
+ public function thumbnailByMaxWH($maxW, $maxH) {
+ $this->tranParams[] = "/thumbnail/" . $maxW . "x" . $maxH;
+ }
+
+ /**
+ * 限定缩略图的宽度和高度的最小值分别为 Width 和 Height,进行等比缩放
+ * @param $minW
+ * @param $minH
+ */
+ public function thumbnailByMinWH($minW, $minH) {
+ $this->tranParams[] = "/thumbnail/!" . $minW . "x" . $minH . "r" ;
+ }
+
+ /**
+ * 忽略原图宽高比例,指定图片宽度为 Width,高度为 Height,强行缩放图片,可能导致目标图片变形
+ * @param $width
+ * @param $height
+ */
+ public function thumbnailByWH($width, $height) {
+ $this->tranParams[] = "/thumbnail/" . $width . "x" . $height . "!";
+ }
+
+ /**
+ * 限定缩略图的宽度和高度的最大值分别为 Width 和 Height,进行等比缩小,比例值为宽缩放比和高缩放比的较小值,如果目标宽(高)都大于原图宽(高),则不变
+ * @param $width
+ * @param $height
+ */
+ public function thumbnailEqualRatioReduceByWH($width, $height) {
+ $this->tranParams[] = "/thumbnail/{$width}x{$height}>";
+ }
+
+ /**
+ * 限定缩略图的宽度和高度的最大值分别为 Width 和 Height,进行等比放大,比例值为宽缩放比和高缩放比的较小值。如果目标宽(高)小于原图宽(高),则不变
+ * @param $width
+ * @param $height
+ */
+ public function thumbnailEqualRatioEnlargeByWH($width, $height) {
+ $this->tranParams[] = "/thumbnail/{$width}x{$height}<";
+ }
+
+ /**
+ * 等比缩放图片,缩放后的图像,总像素数量不超过 $pixel
+ * @param $pixel
+ */
+ public function thumbnailByPixel($pixel) {
+ $this->tranParams[] = "/thumbnail/" . $pixel . "@";
+ }
+
+ /**
+ * 将原图缩放为指定 Width 和 Height 的矩形内的最大图片,之后使用 color 参数指定的颜色居中填充空白部分;取值0或1,0代表不使用 pad 模式,1代表使用 pad 模式
+ * @param $is
+ */
+ public function pad($is) {
+ $this->tranParams[] = "/pad/{$is}";
+ }
+
+ /**
+ * 填充颜色,缺省为白色,需设置为十六进制 RGB 格式(如 #FF0000),详情参考 RGB 编码表,需经过 URL 安全的 Base64 编码,默认值为 #3D3D3D
+ * @param $rgb
+ */
+ public function color($rgb) {
+ $rgb = $this->ciBase64($rgb);
+ $this->tranParams[] = "/color/{$rgb}";
+ }
+
+ /**
+ * 当处理参数中携带此参数时,针对文件过大、参数超限等导致处理失败的场景,会直接返回原图而不报错
+ */
+ public function ignoreError() {
+ $this->tranParams[] = "/ignore-error/1";
+ }
+
+ /**
+ * 普通裁剪参数说明 操作名称:cut
+ * @param $width
+ * @param $height
+ * @param $dx
+ * @param $dy
+ */
+ public function cut($width, $height, $dx, $dy) {
+ $this->tranParams[] = "/cut/" . $width . "x" . "$height" . "x" . $dx . "x" . $dy;
+ }
+
+ /**
+ * 指定目标图片宽度为 Width,高度不变。Width 取值范围应大于0,小于原图宽度
+ * @param $width
+ * @param string $gravity 指定操作的起点位置
+ */
+ public function cropByWidth($width, $gravity = "") {
+ $temp = "/crop/" . $width . "x";
+ if($gravity){
+ $temp .= "/gravity/" . $gravity;
+ }
+ $this->tranParams[] = $temp;
+ }
+
+ /**
+ * 指定目标图片高度为 Height,宽度不变。Height 取值范围应大于0,小于原图高度
+ * @param $height
+ * @param string $gravity 指定操作的起点位置
+ */
+ public function cropByHeight($height, $gravity = "") {
+ $temp = "/crop/x" . $height;
+ if($gravity){
+ $temp .= "/gravity/" . $gravity;
+ }
+ $this->tranParams[] = $temp;
+ }
+
+ /**
+ * 指定目标图片宽度为 Width,高度为 Height 。Width 和 Height 取值范围都应大于0,小于原图宽度/高度
+ * @param $width
+ * @param $height
+ * @param string $gravity 指定操作的起点位置
+ */
+ public function cropByWH($width, $height, $gravity = "") {
+ $temp = "/crop/" . $width . "x" . $height;
+ if($gravity){
+ $temp .= "/gravity/" . $gravity;
+ }
+ $this->tranParams[] = $temp;
+ }
+
+ /**
+ * 内切圆裁剪功能,radius 是内切圆的半径,取值范围为大于0且小于原图最小边一半的整数。内切圆的圆心为图片的中心。图片格式为 gif 时,不支持该参数。
+ * @param $radius
+ */
+ public function iradius($radius) {
+ $this->tranParams[] = "/iradius/" . $radius;
+ }
+
+ /**
+ * 圆角裁剪功能,radius 为图片圆角边缘的半径,取值范围为大于0且小于原图最小边一半的整数。圆角与原图边缘相切。图片格式为 gif 时,不支持该参数。
+ * @param $radius
+ */
+ public function rradius($radius) {
+ $this->tranParams[] = "/rradius/" . $radius;
+ }
+
+ /**
+ * 基于图片中的人脸位置进行缩放裁剪。目标图片的宽度为 Width、高度为 Height。
+ * @param $width
+ * @param $height
+ */
+ public function scrop($width, $height) {
+ $this->tranParams[] = "/scrop/" . $width . "x" . $height;
+ }
+
+ /**
+ * 普通旋转:图片顺时针旋转角度,取值范围0 - 360,默认不旋转。
+ * @param $degree
+ */
+ public function rotate($degree) {
+ $this->tranParams[] = "/rotate/" . $degree;
+ }
+
+ /**
+ * 自适应旋转:根据原图 EXIF 信息将图片自适应旋转回正。
+ */
+ public function autoOrient() {
+ $this->tranParams[] = "/auto-orient";
+ }
+
+ /**
+ * 镜像翻转:flip 值为 vertical 表示垂直翻转,horizontal 表示水平翻转
+ * @param $flip
+ */
+ public function flip($flip) {
+ $this->tranParams[] = "/flip/" . $flip;
+ }
+
+ /**
+ * 格式转换:目标缩略图的图片格式可为:jpg,bmp,gif,png,webp,yjpeg 等,其中 yjpeg 为数据万象针对 jpeg 格式进行的优化,本质为 jpg 格式;缺省为原图格式。
+ * @param $format
+ */
+ public function format($format) {
+ $this->tranParams[] = "/format/" . $format;
+ }
+
+ /**
+ * gif 格式优化:只针对原图为 gif 格式,对 gif 图片格式进行的优化,降帧降颜色。分为以下两种情况:
+ * FrameNumber=1,则按照默认帧数30处理,如果图片帧数大于该帧数则截取。
+ * FrameNumber 取值( 1,100 ],则将图片压缩到指定帧数 (FrameNumber)。
+ * @param $frameNumber
+ */
+ public function gifOptimization($frameNumber) {
+ $this->tranParams[] = "/cgif/" . $frameNumber;
+ }
+
+ /**
+ * 输出为渐进式 jpg 格式。Mode 可为0或1。0:表示不开启渐进式;1:表示开启渐进式。该参数仅在输出图片格式为 jpg 格式时有效。如果输出非 jpg 图片格式,会忽略该参数,默认值0。
+ * @param $mode
+ */
+ public function jpegInterlaceMode($mode) {
+ $this->tranParams[] = "/interlace/" . $mode;
+ }
+
+ /**
+ * 图片的绝对质量,取值范围0 - 100,默认值为原图质量;取原图质量和指定质量的最小值;后面加“!”表示强制使用指定值,例如:90!。
+ * @param $value
+ * @param int $force
+ */
+ public function quality($value, $force = 0) {
+ $temp = "/quality/" . $value;
+ if($force){
+ $temp .= "!";
+ }
+ $this->tranParams[] = $temp;
+ }
+
+ /**
+ * 图片的最低质量,取值范围0 - 100,设置结果图的质量参数最小值。
+ * 例如原图质量为85,将 lquality 设置为80后,处理结果图的图片质量为85。
+ * 例如原图质量为60,将 lquality 设置为80后,处理结果图的图片质量会被提升至80。
+ * @param $value
+ */
+ public function lowestQuality($value) {
+ $this->tranParams[] = "/lquality/" . $value;
+ }
+
+ /**
+ * 图片的相对质量,取值范围0 - 100,数值以原图质量为标准。例如原图质量为80,将 rquality 设置为80后,得到处理结果图的图片质量为64(80x80%)。
+ * @param $value
+ */
+ public function relativelyQuality($value) {
+ $this->tranParams[] = "/rquality/" . $value;
+ }
+
+ /**
+ * 高斯模糊
+ * @param $radius integer|float 模糊半径,取值范围为1 - 50
+ * @param $sigma integer|float 正态分布的标准差,必须大于0
+ */
+ public function blur($radius, $sigma) {
+ $this->tranParams[] = "/blur/" . $radius . "x" . $sigma;
+ }
+
+ /**
+ * 图片亮度调节功能,value 为亮度参数值,取值范围为[-100, 100]的整数。
+ * 取值<0:降低图片亮度。
+ * 取值 = 0:不调整图片亮度。
+ * 取值>0:提高图片亮度。
+ * @param $value
+ */
+ public function bright($value) {
+ $this->tranParams[] = "/bright/" . $value;
+ }
+
+ /**
+ * 图片对比度调节功能,value 为对比度参数值,取值范围为[-100, 100]的整数。
+ * 取值<0:降低图片对比度。
+ * 取值 = 0:不调整图片对比度。
+ * 取值>0:提高图片对比度。
+ * @param $value
+ */
+ public function contrast($value) {
+ $this->tranParams[] = "/contrast/" . $value;
+ }
+
+ /**
+ * 图片锐化功能,value 为锐化参数值,取值范围为10 - 300间的整数(推荐使用70)。参数值越大,锐化效果越明显。
+ * @param $value
+ */
+ public function sharpen($value) {
+ $this->tranParams[] = "/sharpen/" . $value;
+ }
+
+ /**
+ * 将图片设置为灰度图。 value 取值为0表示不改变图片。 value 取值为1表示将图片变为灰度图。
+ * @param $value
+ */
+ public function grayscale($value) {
+ $this->tranParams[] = "/grayscale/" . $value;
+ }
+
+ /**
+ * 去除图片元信息,包括 exif 信息
+ */
+ public function strip() {
+ $this->tranParams[] = "/strip";
+ }
+
+ /**
+ * 限制图片转换后的大小,支持以兆字节(m)和千字节(k)为单位
+ * 1. 仅支持 JPG 格式的图片,可以用于限制处理后图片的大小
+ * 2. 若在尾部加上!,表示用处理后的图片大小与原图大小做比较,如果处理后的图片比原图小,则返回处理后的图片,否则返回原图。例如:examplebucket-1250000000.cos.ap-shanghai.myqcloud.com/picture.jpg?imageMogr2/size-limit/15k!
+ * 3. 建议搭配strip参数使用,去除图片的一些冗余信息,会有更好的效果。例如:examplebucket-1250000000.cos.ap-shanghai.myqcloud.com/picture.jpg?imageMogr2/strip/format/png/size-limit/15k!
+ * @param $value
+ * @param int $compare
+ */
+ public function sizeLimit($value, $compare = 0) {
+ $temp = "/size-limit/" . $value;
+ if($compare){
+ $temp .= "!";
+ }
+ $this->tranParams[] = $temp;
+ }
+
+ public function queryString() {
+ if($this->tranParams) {
+ $this->tranString = "imageMogr2" . implode("", $this->tranParams);
+ }
+ return $this->tranString;
+ }
+
+ public function resetRule() {
+ $this->tranString = "";
+ $this->tranParams = array();
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageQrcodeTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageQrcodeTemplate.php
new file mode 100644
index 0000000..606168b
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageQrcodeTemplate.php
@@ -0,0 +1,37 @@
+mode = "";
+ }
+
+ public function setMode($mode) {
+ $this->mode = "/cover/" . $mode;
+ }
+
+ public function getMode() {
+ return $this->mode;
+ }
+
+ public function queryString() {
+ $head = "QRcode";
+ $res = "";
+ if($this->mode) {
+ $res .= $this->mode;
+ }
+ if($res) {
+ $res = $head . $res;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->mode = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageStyleTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageStyleTemplate.php
new file mode 100644
index 0000000..c8fa533
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageStyleTemplate.php
@@ -0,0 +1,33 @@
+style = "";
+ }
+
+ public function setStyle($styleName) {
+ $this->style = "style/" . $styleName;
+ }
+
+ public function getStyle() {
+ return $this->style;
+ }
+
+ public function queryString() {
+ $res = "";
+ if($this->style) {
+ $res = $this->style;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->style = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageTemplate.php
new file mode 100644
index 0000000..eabe676
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageTemplate.php
@@ -0,0 +1,18 @@
+mode = "";
+ $this->width = "";
+ $this->height = "";
+ $this->format = "";
+ $this->quality = "";
+ $this->ignoreError = "";
+ }
+
+ public function setMode($value) {
+ $this->mode = "/" . $value;
+ }
+
+ public function setWidth($value) {
+ $this->width = "/w/" . $value;
+ }
+
+ public function setHeight($value) {
+ $this->height = "/h/" . $value;
+ }
+
+ public function setFormat($value) {
+ $this->format = "/format/" . $value;
+ }
+
+ public function setQuality($qualityType, $qualityValue, $force = 0) {
+ if($qualityType == 1){
+ $this->quality = "/q/$qualityValue" ;
+ if($force){
+ $this->quality .= "!";
+ }
+ }else if($qualityType == 2){
+ $this->quality = "/rq/$qualityValue" ;
+ }else if ($qualityType == 3){
+ $this->quality = "/lq/$qualityValue" ;
+ }
+ }
+
+ public function ignoreError() {
+ $this->ignoreError = '/ignore-error/1';
+ }
+
+ public function getMode() {
+ return $this->mode;
+ }
+
+ public function getWidth() {
+ return $this->width;
+ }
+
+ public function getHeight() {
+ return $this->height;
+ }
+
+ public function getFormat() {
+ return $this->format;
+ }
+
+ public function getQuality() {
+ return $this->quality;
+ }
+
+ public function queryString() {
+ $head = "imageView2";
+ $res = "";
+ if($this->mode) {
+ $res .= $this->mode;
+ }
+ if($this->width) {
+ $res .= $this->width;
+ }
+ if($this->height) {
+ $res .= $this->height;
+ }
+ if($this->format) {
+ $res .= $this->format;
+ }
+ if($this->quality) {
+ $res .= $this->quality;
+ }
+ if($this->ignoreError) {
+ $res .= $this->ignoreError;
+ }
+ if($res) {
+ $res = $head . $res;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->mode = "";
+ $this->width = "";
+ $this->height = "";
+ $this->format = "";
+ $this->quality = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageWatermarkTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageWatermarkTemplate.php
new file mode 100644
index 0000000..8906943
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/ImageWatermarkTemplate.php
@@ -0,0 +1,212 @@
+image = "";
+ $this->gravity = "";
+ $this->dx = "";
+ $this->dy = "";
+ $this->blogo = "";
+ $this->scatype = "";
+ $this->spcent = "";
+ $this->dissolve = "";
+ $this->batch = "";
+ $this->degree = "";
+ }
+
+ /**
+ * 水印图片地址,需要经过 URL 安全的 Base64 编码。
+ * @param $value
+ */
+ public function setImage($value) {
+ $this->image = "/image/" . $this->ciBase64($value);
+ }
+
+ /**
+ * 图片水印位置,九宫格位置(参考九宫格方位图 ),默认值 SouthEast
+ * @param $value
+ */
+ public function setGravity($value) {
+ $this->gravity = "/gravity/" . $value;
+ }
+
+ /**
+ * 水平(横轴)边距,单位为像素,缺省值为0
+ * @param $value
+ */
+ public function setDx($value) {
+ $this->dx = "/dx/" . $value;
+ }
+
+ /**
+ * 垂直(纵轴)边距,单位为像素,默认值为0
+ * @param $value
+ */
+ public function setDy($value) {
+ $this->dy = "/dy/" . $value;
+ }
+
+ /**
+ * 水印图适配功能,适用于水印图尺寸过大的场景(如水印墙)。共有两种类型:
+ * 当 blogo 设置为1时,水印图会被缩放至与原图相似大小后添加
+ * 当 blogo 设置为2时,水印图会被直接裁剪至与原图相似大小后添加
+ * @param $value
+ */
+ public function setBlogo($value) {
+ $this->blogo = "/blogo/" . $value;
+ }
+
+ /**
+ * 根据原图的大小,缩放调整水印图的大小:
+ * 当 scatype 设置为1时,按原图的宽缩放
+ * 当 scatype 设置为2时,按原图的高缩放
+ * 当 scatype 设置为3时,按原图的整体面积缩放
+ * @param $value
+ */
+ public function setScatype($value) {
+ $this->scatype = "/scatype/" . $value;
+ }
+
+ /**
+ * 与 scatype 搭配使用:
+ * 当 scatype 设置为1时,该有效值为[1,1000],单位为千分比
+ * 当 scatype 设置为2时,该有效值为[1,1000],单位为千分比
+ * 当 scatype 设置为3时,该有效值为[1,250],单位为千分比。
+ * @param $value
+ */
+ public function setSpcent($value) {
+ $this->spcent = "/spcent/" . $value;
+ }
+
+ /**
+ * 图片水印的透明度,取值为1 - 100,默认90(90%不透明度)
+ * @param $value
+ */
+ public function setDissolve($value) {
+ $this->dissolve = "/dissolve/" . $value;
+ }
+
+ /**
+ * 平铺水印功能,可将图片水印平铺至整张图片。值为1时,表示开启平铺水印功能
+ * @param $value
+ */
+ public function setBatch($value) {
+ $this->batch = "/batch/" . $value;
+ }
+
+ /**
+ * 当 batch 值为1时生效。图片水印的旋转角度设置,取值范围为0 - 360,默认0
+ * @param $value
+ */
+ public function setDegree($value) {
+ $this->degree = "/degree/" . $value;
+ }
+
+ public function getImage() {
+ return $this->image;
+ }
+
+ public function getGravity() {
+ return $this->gravity;
+ }
+
+ public function getDx() {
+ return $this->dx;
+ }
+
+ public function getDy() {
+ return $this->dy;
+ }
+
+ public function getBlogo() {
+ return $this->blogo;
+ }
+
+ public function getScatype() {
+ return $this->scatype;
+ }
+
+ public function getSpcent() {
+ return $this->spcent;
+ }
+
+ public function getDissolve() {
+ return $this->dissolve;
+ }
+
+ public function getBatch() {
+ return $this->batch;
+ }
+
+ public function getDegree() {
+ return $this->degree;
+ }
+
+ public function queryString() {
+ $head = "watermark/1";
+ $res = "";
+ if($this->image) {
+ $res .= $this->image;
+ }
+ if($this->gravity) {
+ $res .= $this->gravity;
+ }
+ if($this->dx) {
+ $res .= $this->dx;
+ }
+ if($this->dy) {
+ $res .= $this->dy;
+ }
+ if($this->blogo) {
+ $res .= $this->blogo;
+ }
+ if($this->scatype) {
+ $res .= $this->scatype;
+ }
+ if($this->spcent) {
+ $res .= $this->spcent;
+ }
+ if($this->dissolve) {
+ $res .= $this->dissolve;
+ }
+ if($this->batch) {
+ $res .= $this->batch;
+ }
+ if($this->degree) {
+ $res .= $this->degree;
+ }
+ if($res) {
+ $res = $head . $res;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->image = "";
+ $this->gravity = "";
+ $this->dx = "";
+ $this->dy = "";
+ $this->blogo = "";
+ $this->scatype = "";
+ $this->spcent = "";
+ $this->dissolve = "";
+ $this->batch = "";
+ $this->degree = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/PicOperationsTransformation.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/PicOperationsTransformation.php
new file mode 100644
index 0000000..a2f10de
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/PicOperationsTransformation.php
@@ -0,0 +1,64 @@
+isPicInfo = 0;
+ $this->rules = array();
+ }
+
+ public function setIsPicInfo($value) {
+ $this->isPicInfo = $value;
+ }
+
+ public function addRule(ImageTemplate $template, $fileid = "", $bucket = "") {
+ $rule = $template->queryString();
+ if($rule){
+ $item = array();
+ $item['rule'] = $rule;
+ if($fileid){
+ $item['fileid'] = $fileid;
+ }
+ if($bucket) {
+ $item['bucket'] = $bucket;
+ }
+ $this->rules[] = $item;
+ }
+ }
+
+ public function getIsPicInfo() {
+ return $this->isPicInfo;
+ }
+
+ public function getRules() {
+ return $this->rules;
+ }
+
+ public function queryString() {
+ $res = "";
+ $picOperations = array();
+ if($this->isPicInfo){
+ $picOperations['is_pic_info'] = $this->isPicInfo;
+ }
+ if($this->rules){
+ $picOperations['rules'] = $this->rules;
+ }
+ if($picOperations){
+ $res = json_encode($picOperations);
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->isPicInfo = 0;
+ $this->rules = array();
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/TextWatermarkTemplate.php b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/TextWatermarkTemplate.php
new file mode 100644
index 0000000..7f35993
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ImageParamTemplate/TextWatermarkTemplate.php
@@ -0,0 +1,180 @@
+text = "";
+ $this->font = "";
+ $this->fontsize = "";
+ $this->fill = "";
+ $this->dissolve = "";
+ $this->gravity = "";
+ $this->dx = "";
+ $this->dy = "";
+ $this->batch = "";
+ $this->degree = "";
+ $this->shadow = "";
+ }
+
+ public function setText($value) {
+ $this->text = "/text/" . $this->ciBase64($value);
+ }
+
+ public function setFont($value) {
+ $this->font = "/font/" . $this->ciBase64($value);
+ }
+
+ public function setFontsize($value) {
+ $this->fontsize = "/fontsize/" . $value;
+ }
+
+ public function setFill($value) {
+ $this->fill = "/fill/" . $this->ciBase64($value);
+ }
+
+ public function setDissolve($value) {
+ $this->dissolve = "/dissolve/" . $value;
+ }
+
+ public function setGravity($value) {
+ $this->gravity = "/gravity/" . $value;
+ }
+
+ public function setDx($value) {
+ $this->dx = "/dx/" . $value;
+ }
+
+ public function setDy($value) {
+ $this->dy = "/dy/" . $value;
+ }
+
+ public function setBatch($value) {
+ $this->batch = "/batch/" . $value;
+ }
+
+ public function setDegree($value) {
+ $this->degree = "/degree/" . $value;
+ }
+
+ public function setShadow($value) {
+ $this->shadow = "/shadow/" . $value;
+ }
+
+ public function getText() {
+ return $this->text;
+ }
+
+ public function getFont() {
+ return $this->font;
+ }
+
+ public function getFontsize() {
+ return $this->fontsize;
+ }
+
+ public function getFill() {
+ return $this->fill;
+ }
+
+ public function getDissolve() {
+ return $this->dissolve;
+ }
+
+ public function getGravity() {
+ return $this->gravity;
+ }
+
+ public function getDx() {
+ return $this->dx;
+ }
+
+ public function getDy() {
+ return $this->dy;
+ }
+
+ public function getBatch() {
+ return $this->batch;
+ }
+
+ public function getDegree() {
+ return $this->degree;
+ }
+
+ public function getShadow() {
+ return $this->shadow;
+ }
+
+ public function queryString() {
+ $head = "watermark/2";
+ $res = "";
+ if($this->text) {
+ $res .= $this->text;
+ }
+ if($this->font) {
+ $res .= $this->font;
+ }
+ if($this->fontsize) {
+ $res .= $this->fontsize;
+ }
+ if($this->fill) {
+ $res .= $this->fill;
+ }
+ if($this->dissolve) {
+ $res .= $this->dissolve;
+ }
+ if($this->gravity) {
+ $res .= $this->gravity;
+ }
+ if($this->dx) {
+ $res .= $this->dx;
+ }
+ if($this->dy) {
+ $res .= $this->dy;
+ }
+ if($this->batch) {
+ $res .= $this->batch;
+ }
+ if($this->degree) {
+ $res .= $this->degree;
+ }
+ if($this->shadow) {
+ $res .= $this->shadow;
+ }
+ if($res) {
+ $res = $head . $res;
+ }
+ return $res;
+ }
+
+ public function resetRule() {
+ $this->text = "";
+ $this->font = "";
+ $this->fontsize = "";
+ $this->fill = "";
+ $this->dissolve = "";
+ $this->gravity = "";
+ $this->dx = "";
+ $this->dy = "";
+ $this->batch = "";
+ $this->degree = "";
+ $this->shadow = "";
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/MultipartUpload.php b/server/addons/cos/library/Qcloud/Cos/MultipartUpload.php
new file mode 100644
index 0000000..2eec9c0
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/MultipartUpload.php
@@ -0,0 +1,167 @@
+body = $body;
+ $this->client = $client;
+ $this->options = $options;
+ $this->partSize = $this->calculatePartSize($minPartSize);
+ $this->concurrency = isset($options['Concurrency']) ? $options['Concurrency'] : 10;
+ $this->progress = isset($options['Progress']) ? $options['Progress'] : function($totalSize, $uploadedSize) {};
+ $this->parts = [];
+ $this->partNumberList = [];
+ $this->uploadedSize = 0;
+ $this->totalSize = $this->body->getSize();
+ $this->needMd5 = isset($options['ContentMD5']) ? $options['ContentMD5'] : true;
+ $this->retry = isset($options['Retry']) ? $options['Retry'] : 3;
+ }
+ public function performUploading() {
+ $uploadId= $this->initiateMultipartUpload();
+ $this->uploadParts($uploadId);
+ foreach ( $this->parts as $key => $row ){
+ $num1[$key] = $row ['PartNumber'];
+ $num2[$key] = $row ['ETag'];
+ }
+ array_multisort($num1, SORT_ASC, $num2, SORT_ASC, $this->parts);
+ return $this->client->completeMultipartUpload(array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'UploadId' => $uploadId,
+ 'Parts' => $this->parts)
+ );
+
+ }
+ public function uploadParts($uploadId) {
+ $uploadRequests = function ($uploadId) {
+ $partNumber = 1;
+ $index = 1;
+ $offset = 0;
+ $partSize = 0;
+ for ( ; ; $partNumber ++) {
+ if ($this->body->eof()) {
+ break;
+ }
+ $body = $this->body->read($this->partSize);
+ $partSize = $this->partSize;
+ if ($offset + $this->partSize >= $this->totalSize) {
+ $partSize = $this->totalSize - $offset;
+ }
+ $offset += $partSize;
+ if (empty($body)) {
+ break;
+ }
+ if (isset($this->parts[$partNumber])) {
+ continue;
+ }
+ $this->partNumberList[$index]['PartNumber'] = $partNumber;
+ $this->partNumberList[$index]['PartSize'] = $partSize;
+ $params = array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'UploadId' => $uploadId,
+ 'PartNumber' => $partNumber,
+ 'Body' => $body,
+ 'ContentMD5' => $this->needMd5
+ );
+ if ($this->needMd5 == false) {
+ unset($params["ContentMD5"]);
+ }
+ if (!isset($this->parts[$partNumber])) {
+ $command = $this->client->getCommand('uploadPart', $params);
+ $request = $this->client->commandToRequestTransformer($command);
+ $index ++;
+ yield $request;
+ }
+ }
+ };
+ $pool = new Pool($this->client->httpClient, $uploadRequests($uploadId), [
+ 'concurrency' => $this->concurrency,
+ 'fulfilled' => function ($response, $index) {
+ $index = $index + 1;
+ $partNumber = $this->partNumberList[$index]['PartNumber'];
+ $partSize = $this->partNumberList[$index]['PartSize'];
+
+ //兼容两种写法,防止index为undefined
+ if (array_key_exists('etag', $response->getHeaders())) {
+ $etag = $response->getHeaders()["etag"][0];
+ }
+
+ if (array_key_exists('ETag', $response->getHeaders())) {
+ $etag = $response->getHeaders()["ETag"][0];
+ }
+ $part = array('PartNumber' => $partNumber, 'ETag' => $etag);
+ $this->parts[$partNumber] = $part;
+ $this->uploadedSize += $partSize;
+ call_user_func_array($this->progress, [$this->totalSize, $this->uploadedSize]);
+ },
+
+ 'rejected' => function ($reason, $index) {
+ printf("part [%d] upload failed, reason: %s\n", $index, $reason);
+ throw($reason);
+ }
+ ]);
+ $promise = $pool->promise();
+ $promise->wait();
+ }
+
+ public function resumeUploading() {
+ $uploadId = $this->options['UploadId'];
+ $rt = $this->client->ListParts(
+ array('UploadId' => $uploadId,
+ 'Bucket'=>$this->options['Bucket'],
+ 'Key'=>$this->options['Key']));
+ $parts = array();
+ if (count($rt['Parts']) > 0) {
+ foreach ($rt['Parts'] as $part) {
+ $this->parts[$part['PartNumber']] = array('PartNumber' => $part['PartNumber'], 'ETag' => $part['ETag']);
+ }
+ }
+ $this->uploadParts($uploadId);
+ foreach ( $this->parts as $key => $row ){
+ $num1[$key] = $row ['PartNumber'];
+ $num2[$key] = $row ['ETag'];
+ }
+ array_multisort($num1, SORT_ASC, $num2, SORT_ASC, $this->parts);
+ return $this->client->completeMultipartUpload(array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'UploadId' => $uploadId,
+ 'Parts' => $this->parts)
+ );
+ }
+
+ private function calculatePartSize($minPartSize)
+ {
+ $partSize = intval(ceil(($this->body->getSize() / self::MAX_PARTS)));
+ $partSize = max($minPartSize, $partSize);
+ $partSize = min($partSize, self::MAX_PART_SIZE);
+ $partSize = max($partSize, self::MIN_PART_SIZE);
+ return $partSize;
+ }
+
+ private function initiateMultipartUpload() {
+ $result = $this->client->createMultipartUpload($this->options);
+ return $result['UploadId'];
+ }
+
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/RangeDownload.php b/server/addons/cos/library/Qcloud/Cos/RangeDownload.php
new file mode 100644
index 0000000..77f50f7
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/RangeDownload.php
@@ -0,0 +1,145 @@
+client = $client;
+ $this->options = $options;
+ $this->partSize = isset( $options['PartSize'] ) ? $options['PartSize'] : self::DEFAULT_PART_SIZE;
+ $this->concurrency = isset( $options['Concurrency'] ) ? $options['Concurrency'] : 10;
+ $this->progress = isset( $options['Progress'] ) ? $options['Progress'] : function( $totalSize, $downloadedSize ) {
+ }
+ ;
+ $this->parts = [];
+ $this->partNumberList = [];
+ $this->downloadedSize = 0;
+ $this->totalSize = $contentLength;
+ $this->saveAs = $saveAs;
+ $this->resumableJson = [];
+ $this->resumableJson = isset( $options['ResumableJson'] ) ? $options['ResumableJson'] : [];
+ unset( $options['ResumableJson'] );
+ $this->resumableTaskFile = isset( $options['ResumableTaskFile'] ) ? $options['ResumableTaskFile'] : $saveAs . '.cosresumabletask';
+ $this->resumableDownload = isset( $options['ResumableDownload'] ) ? $options['ResumableDownload'] : false;
+ }
+
+ public function performdownloading() {
+ if ( $this->resumableDownload ) {
+ try {
+ if ( file_exists( $this->resumableTaskFile ) ) {
+ $origin_content = file_get_contents( $this->resumableTaskFile );
+ $this->resumableJsonLocal = json_decode( $origin_content, true );
+ if ( $this->resumableJsonLocal == null ) {
+ $this->resumableJsonLocal = [];
+ } else if ( $this->resumableJsonLocal['LastModified'] != $this->resumableJson['LastModified'] ||
+ $this->resumableJsonLocal['ContentLength'] != $this->resumableJson['ContentLength'] ||
+ $this->resumableJsonLocal['ETag'] != $this->resumableJson['ETag'] ||
+ $this->resumableJsonLocal['Crc64ecma'] != $this->resumableJson['Crc64ecma'] ) {
+ $this->resumableDownload = false;
+ }
+ }
+ } catch ( \Exception $e ) {
+ $this->resumableDownload = false;
+ }
+ }
+ try {
+ if ($this->resumableDownload) {
+ $this->fp = fopen( $this->saveAs, 'r+' );
+ } else {
+ $this->fp = fopen( $this->saveAs, 'wb' );
+ }
+ $rt = $this->donwloadParts();
+ $this->resumableJson['DownloadedBlocks'] = [];
+ if (file_exists( $this->resumableTaskFile )) {
+ unlink($this->resumableTaskFile);
+ }
+ } catch ( \Exception $e ) {
+ $this->fp_resume = fopen( $this->resumableTaskFile, 'wb' );
+ fwrite( $this->fp_resume, json_encode( $this->resumableJson ) );
+ fclose( $this->fp_resume );
+ throw ( $e );
+ }
+ finally {
+ fclose( $this->fp );
+ }
+ return $rt;
+ }
+
+ public function donwloadParts() {
+ $uploadRequests = function () {
+ $index = 1;
+ $partSize = 0;
+ for ( $offset = 0; $offset < $this->totalSize; ) {
+ $partSize = $this->partSize;
+ if ( $offset + $this->partSize >= $this->totalSize ) {
+ $partSize = $this->totalSize - $offset;
+ }
+ $this->parts[$index]['PartSize'] = $partSize;
+ $this->parts[$index]['Offset'] = $offset;
+ $begin = $offset;
+ $end = $offset + $partSize - 1;
+ if ( !( $this->resumableDownload &&
+ isset( $this->resumableJsonLocal['DownloadedBlocks'] ) &&
+ in_array( ['from' => $begin, 'to' => $end], $this->resumableJsonLocal['DownloadedBlocks'] ) ) ) {
+ $params = array(
+ 'Bucket' => $this->options['Bucket'],
+ 'Key' => $this->options['Key'],
+ 'Range' => sprintf( 'bytes=%d-%d', $begin, $end )
+ );
+ $command = $this->client->getCommand( 'getObject', $params );
+ $request = $this->client->commandToRequestTransformer( $command );
+ $index += 1;
+ yield $request;
+ } else {
+ $this->resumableJson['DownloadedBlocks'][] = ['from' => $begin, 'to' => $end];
+ $this->downloadedSize += $partSize;
+ call_user_func_array( $this->progress, [$this->totalSize, $this->downloadedSize] );
+ }
+ $offset += $partSize;
+ }
+
+ }
+ ;
+
+ $pool = new Pool( $this->client->httpClient, $uploadRequests(), [
+ 'concurrency' => $this->concurrency,
+ 'fulfilled' => function ( $response, $index ) {
+ $index = $index + 1;
+ $stream = $response->getBody();
+ $offset = $this->parts[$index]['Offset'];
+ $partsize = 8192;
+ $begin = $offset;
+ fseek( $this->fp, $offset );
+ while ( !$stream->eof() ) {
+ $output = $stream->read( $partsize );
+ $writeLen = fwrite( $this->fp, $output );
+ $offset += $writeLen;
+ }
+ $end = $offset - 1;
+ $this->resumableJson['DownloadedBlocks'][] = ['from' => $begin, 'to' => $end];
+ $partSize = $this->parts[$index]['PartSize'];
+ $this->downloadedSize += $partSize;
+ call_user_func_array( $this->progress, [$this->totalSize, $this->downloadedSize] );
+ }
+ ,
+ 'rejected' => function ( $reason, $index ) {
+ throw( $reason );
+ }
+ ] );
+ $promise = $pool->promise();
+ $promise->wait();
+ }
+
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Request/BodyLocation.php b/server/addons/cos/library/Qcloud/Cos/Request/BodyLocation.php
new file mode 100644
index 0000000..73787fd
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Request/BodyLocation.php
@@ -0,0 +1,49 @@
+getBody()->getContents();
+ if ('' !== $value) {
+ throw new \RuntimeException('Only one "body" location may exist per operation');
+ }
+ // binary string data from bound parameter
+ $value = $command[$param->getName()];
+ return $request->withBody(Psr7\Utils::streamFor($value));
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/ResultTransformer.php b/server/addons/cos/library/Qcloud/Cos/ResultTransformer.php
new file mode 100644
index 0000000..8ccc28e
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/ResultTransformer.php
@@ -0,0 +1,260 @@
+config = $config;
+ $this->operation = $operation;
+ }
+
+ public function writeDataToLocal(CommandInterface $command, RequestInterface $request, ResponseInterface $response) {
+ $action = $command->getName();
+ if ($action == "GetObject" || $action == "GetSnapshot" || $action == "ImageRepairProcess") {
+ if (isset($command['SaveAs'])) {
+ $fp = fopen($command['SaveAs'], "wb");
+ $stream = $response->getBody();
+ $offset = 0;
+ $partsize = 8192;
+ while (!$stream->eof()) {
+ $output = $stream->read($partsize);
+ fseek($fp, $offset);
+ fwrite($fp, $output);
+ $offset += $partsize;
+ }
+ fclose($fp);
+ }
+ }
+ }
+
+ public function metaDataTransformer(CommandInterface $command, ResponseInterface $response, Result $result) {
+ $headers = $response->getHeaders();
+ $metadata = array();
+ foreach ($headers as $key => $value) {
+ if (strpos($key, "x-cos-meta-") === 0) {
+ $metadata[substr($key, 11)] = $value[0];
+ }
+ }
+ if (!empty($metadata)) {
+ $result['Metadata'] = $metadata;
+ }
+ return $result;
+ }
+
+ public function extraHeadersTransformer(CommandInterface $command, RequestInterface $request, Result $result) {
+ if ($command['Key'] != null && $result['Key'] == null) {
+ $result['Key'] = $command['Key'];
+ }
+ if ($command['Bucket'] != null && $result['Bucket'] == null) {
+ $result['Bucket'] = $command['Bucket'];
+ }
+ $result['Location'] = $request->getHeader("Host")[0] . $request->getUri()->getPath();
+ return $result;
+ }
+
+ public function selectContentTransformer(CommandInterface $command, Result $result) {
+ $action = $command->getName();
+ if ($action == "SelectObjectContent") {
+ $result['Data'] = $this->getSelectContents($result);
+ }
+ return $result;
+ }
+
+ public function ciContentInfoTransformer(CommandInterface $command, Result $result) {
+ $action = $command->getName();
+ if ($action == "ImageInfo" || $action == "ImageExif" || $action == "ImageAve" || $action == "GetPrivateM3U8") {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $result['Data'] = $this->geCiContentInfo($result, $length);
+ }
+ }
+
+ if ($action == "PutObject" && isset($command["PicOperations"]) && $command["PicOperations"]) {
+ $picOperations = json_decode($command["PicOperations"], true);
+ $picRuleSize = isset($picOperations['rules']) && is_array($picOperations['rules']) ? sizeof($picOperations['rules']) : 0;
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $xmlData = json_decode(json_encode($obj),true);
+ if ($picRuleSize == 1 && isset($xmlData['ProcessResults']['Object'])){
+ $tmp = $xmlData['ProcessResults']['Object'];
+ unset($xmlData['ProcessResults']['Object']);
+ $xmlData['ProcessResults']['Object'][] = $tmp;
+ }
+ $result['Data'] = $xmlData;
+ }
+ }
+
+ if ($action == "GetBucketGuetzli" ) {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $arr = json_decode(json_encode($obj),true);
+ $result['GuetzliStatus'] = isset($arr[0]) ? $arr[0] : '';
+ }
+ }
+
+ if ($action == "GetCiService" ) {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $arr = json_decode(json_encode($obj),true);
+ $result['CIStatus'] = isset($arr[0]) ? $arr[0] : '';
+ unset($result['Body']);
+ }
+ }
+
+ if ($action == "GetOriginProtect" ) {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $arr = json_decode(json_encode($obj),true);
+ $result['OriginProtectStatus'] = isset($arr[0]) ? $arr[0] : '';
+ unset($result['Body']);
+ }
+ }
+
+ if ($action == "GetHotLink" ) {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $arr = json_decode(json_encode($obj),true);
+ $result['Hotlink'] = $arr;
+ unset($result['Body']);
+ }
+ }
+
+ $xml2JsonActions = array(
+ 'CreateMediaTranscodeJobs' => 1,
+ 'CreateMediaJobs' => 1,
+ 'DescribeMediaJob' => 1,
+ 'DescribeMediaJobs' => 1,
+ 'CreateMediaSnapshotJobs' => 1,
+ 'CreateMediaConcatJobs' => 1,
+ 'CreateMediaVoiceSeparateJobs' => 1,
+ 'DescribeMediaVoiceSeparateJob' => 1,
+ 'UpdateMediaQueue' => 1,
+ 'CreateMediaSmartCoverJobs' => 1,
+ 'CreateMediaVideoProcessJobs' => 1,
+ 'CreateMediaVideoMontageJobs' => 1,
+ 'CreateMediaAnimationJobs' => 1,
+ 'CreateMediaPicProcessJobs' => 1,
+ 'CreateMediaSegmentJobs' => 1,
+ 'CreateMediaVideoTagJobs' => 1,
+ 'CreateMediaSuperResolutionJobs' => 1,
+ 'CreateMediaSDRtoHDRJobs' => 1,
+ 'CreateMediaDigitalWatermarkJobs' => 1,
+ 'CreateMediaExtractDigitalWatermarkJobs' => 1,
+ 'OpticalOcrRecognition' => 1,
+ 'GetWorkflowInstance' => 1,
+ 'CreateMediaTranscodeTemplate' => 1,
+ 'UpdateMediaTranscodeTemplate' => 1,
+ 'CreateMediaHighSpeedHdTemplate' => 1,
+ 'UpdateMediaHighSpeedHdTemplate' => 1,
+ 'CreateMediaAnimationTemplate' => 1,
+ 'UpdateMediaAnimationTemplate' => 1,
+ 'CreateMediaConcatTemplate' => 1,
+ 'UpdateMediaConcatTemplate' => 1,
+ 'CreateMediaVideoProcessTemplate' => 1,
+ 'UpdateMediaVideoProcessTemplate' => 1,
+ 'CreateMediaVideoMontageTemplate' => 1,
+ 'UpdateMediaVideoMontageTemplate' => 1,
+ 'CreateMediaVoiceSeparateTemplate' => 1,
+ 'UpdateMediaVoiceSeparateTemplate' => 1,
+ 'CreateMediaSuperResolutionTemplate' => 1,
+ 'UpdateMediaSuperResolutionTemplate' => 1,
+ 'CreateMediaPicProcessTemplate' => 1,
+ 'UpdateMediaPicProcessTemplate' => 1,
+ 'CreateMediaWatermarkTemplate' => 1,
+ 'UpdateMediaWatermarkTemplate' => 1,
+ 'CreateInventoryTriggerJob' => 1,
+ 'DescribeInventoryTriggerJobs' => 1,
+ 'DescribeInventoryTriggerJob' => 1,
+ 'CreateMediaNoiseReductionJobs' => 1,
+ 'CreateMediaQualityEstimateJobs' => 1,
+ 'CreateMediaStreamExtractJobs' => 1,
+ );
+ if (key_exists($action, $xml2JsonActions)) {
+ $length = intval($result['ContentLength']);
+ if($length > 0){
+ $content = $this->geCiContentInfo($result, $length);
+ $obj = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
+ $xmlData = json_decode(json_encode($obj),true);
+ $result['Response'] = $xmlData;
+ }
+ }
+
+ return $result;
+ }
+
+ public function geCiContentInfo($result, $length) {
+ $f = $result['Body'];
+ $data = "";
+ while (!$f->eof()) {
+ $tmp = $f->read($length);
+ if (empty($tmp)) {
+ break;
+ }
+ $data .= $tmp;
+ }
+ return $data;
+ }
+
+ public function getSelectContents($result) {
+ $f = $result['RawData'];
+ while (!$f->eof()) {
+ $data = array();
+ $tmp = $f->read(4);
+ if (empty($tmp)) {
+ break;
+ }
+ $total_length = (int)(unpack("N", $tmp)[1]);
+ $headers_length = (int)(unpack("N", $f->read(4))[1]);
+ $body_length = $total_length - $headers_length - 16;
+ $predule_crc = (int)(unpack("N", $f->read(4))[1]);
+ $headers = array();
+ for ($offset = 0; $offset < $headers_length;) {
+ $key_length = (int)(unpack("C", $f->read(1))[1]);
+ $key = $f->read($key_length);
+
+ $head_value_type = (int)(unpack("C", $f->read(1))[1]);
+
+ $value_length = (int)(unpack("n", $f->read(2))[1]);
+ $value = $f->read($value_length);
+ $offset += 4 + $key_length + $value_length;
+ if ($key == ":message-type") {
+ $data['MessageType'] = $value;
+ }
+ if ($key == ":event-type") {
+ $data['EventType'] = $value;
+ }
+ if ($key == ":error-code") {
+ $data['ErrorCode'] = $value;
+ }
+ if ($key == ":error-message") {
+ $data['ErrorMessage'] = $value;
+ }
+ }
+ $body = $f->read($body_length);
+ $message_crc = (int)(unpack("N", $f->read(4))[1]);
+ $data['Body'] = $body;
+ yield $data;
+ }
+ }
+ public function __destruct() {
+ }
+
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Serializer.php b/server/addons/cos/library/Qcloud/Cos/Serializer.php
new file mode 100644
index 0000000..1999e1b
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Serializer.php
@@ -0,0 +1,81 @@
+offsetExists('key') === true) {
+ $mode = empty($command->offsetGet('auth')) === false
+ ? $command->offsetGet('auth')
+ : 'loco';
+ if ($mode !== 'query') {
+ // else use Authorization header of various types
+ if ($mode === 'loco') {
+ $value = 'Loco '.$command->offsetGet('key');
+ $request = $request->withHeader('Authorization', $value);
+ } elseif ($mode === 'basic') {
+ $value = 'Basic '.base64_encode($command->offsetGet('key').':');
+ $request = $request->withHeader('Authorization', $value);
+ } else {
+ throw new \InvalidArgumentException("Invalid auth type: {$mode}");
+ }
+ // avoid request sending key parameter in query string
+ $command->offsetUnset('key');
+ }
+ }
+ // Remap legacy parameters to common `data` binding on request body
+ static $remap = [
+ 'import' => ['src'=>'data'],
+ 'translate' => ['translation'=>'data'],
+ ];
+ $name = $command->getName();
+ if (isset($remap[$name])) {
+ foreach ($remap[$name] as $old => $new) {
+ if ($command->offsetExists($old)) {
+ $command->offsetSet($new, $command->offsetGet($old));
+ $command->offsetUnset($old);
+ }
+ }
+ }
+ */
+ return parent::prepareRequest($command, $request);
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Service.php b/server/addons/cos/library/Qcloud/Cos/Service.php
new file mode 100644
index 0000000..0d03df3
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Service.php
@@ -0,0 +1,7387 @@
+ 'Cos Service',
+ 'apiVersion' => 'V5',
+ 'description' => 'Cos V5 API Service',
+ 'operations' => array(
+ // 舍弃一个分块上传且删除已上传的分片块
+ 'AbortMultipartUpload' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'AbortMultipartUploadOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')),
+ 'UploadId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'uploadId'
+ )
+ )
+ ),
+ // 创建存储桶(Bucket)
+ 'CreateBucket' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateBucketOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CreateBucketConfiguration')),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl'),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ )
+ )
+ ),
+ // 完成整个分块上传
+ 'CompleteMultipartUpload' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CompleteMultipartUploadOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CompleteMultipartUpload'
+ )
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'Parts' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true),
+ 'items' => array(
+ 'name' => 'CompletedPart',
+ 'type' => 'object',
+ 'sentAs' => 'Part',
+ 'properties' => array(
+ 'ETag' => array(
+ 'type' => 'string'
+ ),
+ 'PartNumber' => array(
+ 'type' => 'numeric'
+ )
+ )
+ )
+ ),
+ 'UploadId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'uploadId',
+ ),
+ 'PicOperations' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Pic-Operations',
+ )
+ )
+ ),
+ // 初始化分块上传
+ 'CreateMultipartUpload' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?uploads',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CreateMultipartUploadOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CreateMultipartUploadRequest'
+ )
+ ),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl',
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control',
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition',
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding',
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'Expires' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ ),
+ 'GrantFullControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-full-control',
+ ),
+ 'GrantRead' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read',
+ ),
+ 'GrantReadACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read-acp',
+ ),
+ 'GrantWriteACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write-acp',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ ),
+ 'PicOperations' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Pic-Operations',
+ )
+ )
+ ),
+ // 复制对象
+ 'CopyObject' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'CopyObjectOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CopyObjectRequest',
+ ),
+ ),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl',
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control',
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition',
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding',
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'CopySource' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source',
+ ),
+ 'CopySourceIfMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-match',
+ ),
+ 'CopySourceIfModifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-modified-since',
+ ),
+ 'CopySourceIfNoneMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-none-match',
+ ),
+ 'CopySourceIfUnmodifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-unmodified-since',
+ ),
+ 'Expires' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ ),
+ 'GrantFullControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-full-control',
+ ),
+ 'GrantRead' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read',
+ ),
+ 'GrantReadACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read-acp',
+ ),
+ 'GrantWriteACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write-acp',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'MetadataDirective' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-metadata-directive',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'CopySourceSSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-algorithm',
+ ),
+ 'CopySourceSSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key',
+ ),
+ 'CopySourceSSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key-MD5',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ )
+ ),
+ ),
+ // 删除存储桶 (Bucket)
+ 'DeleteBucket' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ )
+ )
+ ),
+ // 删除跨域访问配置信息
+ 'DeleteBucketCors' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?cors',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketCorsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 删除存储桶标签信息
+ 'DeleteBucketTagging' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketTaggingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 删除存储桶标清单任务
+ 'DeleteBucketInventory' => array(
+ 'httpMethod' => 'Delete',
+ 'uri' => '/{Bucket}?inventory',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketInventoryOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Id' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'id',
+ )
+ ),
+ ),
+ // 删除 COS 上单个对象
+ 'DeleteObject' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteObjectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'MFA' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-mfa',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ )
+ )
+ ),
+ // 批量删除 COS 对象
+ 'DeleteObjects' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}?delete',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteObjectsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Delete',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Objects' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'sentAs' => 'Object',
+ 'properties' => array(
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'minLength' => 1,
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'Quiet' => array(
+ 'type' => 'boolean',
+ 'format' => 'boolean-string',
+ 'location' => 'xml',
+ ),
+ 'MFA' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-mfa',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ )
+ ),
+ ),
+ // 删除存储桶(Bucket)的website
+ 'DeleteBucketWebsite' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?website',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketWebsiteOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 删除存储桶(Bucket)的生命周期配置
+ 'DeleteBucketLifecycle' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?lifecycle',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketLifecycleOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 删除跨区域复制配置
+ 'DeleteBucketReplication' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}?replication',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketReplicationOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 配置对象标签
+ 'PutObjectTagging' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutObjectTaggingOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Tagging',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'TagSet' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'TagRule',
+ 'type' => 'object',
+ 'sentAs' => 'Tag',
+ 'properties' => array(
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 获取对象标签信息
+ 'GetObjectTagging' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetObjectTaggingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ )
+ ),
+ ),
+ // 删除对象标签
+ 'DeleteObjectTagging' => array(
+ 'httpMethod' => 'DELETE',
+ 'uri' => '/{Bucket}{/Key*}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteObjectTaggingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ )
+ )
+ ),
+ // 下载对象
+ 'GetObject' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetObjectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'IfMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'If-Match'
+ ),
+ 'IfModifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer'
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'If-Modified-Since'
+ ),
+ 'IfNoneMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'If-None-Match'
+ ),
+ 'IfUnmodifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer'
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'If-Unmodified-Since'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'Range' => array(
+ 'type' => 'string',
+ 'location' => 'header'),
+ 'ResponseCacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'response-cache-control'
+ ),
+ 'ResponseContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'response-content-disposition'
+ ),
+ 'ResponseContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'response-content-encoding'
+ ),
+ 'ResponseContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'response-content-language'
+ ),
+ 'ResponseContentType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'response-content-type'
+ ),
+ 'ResponseExpires' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer'
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'query',
+ 'sentAs' => 'response-expires'
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'TrafficLimit' => array(
+ 'type' => 'integer',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-traffic-limit',
+ )
+ )
+ ),
+ // 获取 COS 对象的访问权限信息(Access Control List, ACL)
+ 'GetObjectAcl' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?acl',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetObjectAclOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ )
+ )
+ ),
+ // 获取存储桶(Bucket)的访问权限信息(Access Control List, ACL)
+ 'GetBucketAcl' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?acl',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketAclOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ )
+ )
+ ),
+ // 查询存储桶(Bucket)跨域访问配置信息
+ 'GetBucketCors' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?cors',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketCorsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 查询存储桶(Bucket)Domain配置信息
+ 'GetBucketDomain' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?domain',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketDomainOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 查询存储桶(Bucket)Accelerate配置信息
+ 'GetBucketAccelerate' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?accelerate',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketAccelerateOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 查询存储桶(Bucket)Website配置信息
+ 'GetBucketWebsite' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?website',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketWebsiteOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 查询存储桶(Bucket)的生命周期配置
+ 'GetBucketLifecycle' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?lifecycle',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketLifecycleOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)版本控制信息
+ 'GetBucketVersioning' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?versioning',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketVersioningOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)跨区域复制配置信息
+ 'GetBucketReplication' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?replication',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketReplicationOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)所在的地域信息
+ 'GetBucketLocation' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?location',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketLocationOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ // 获取存储桶(Bucket)Notification信息
+ 'GetBucketNotification' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?notification',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketNotificationOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)日志信息
+ 'GetBucketLogging' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?logging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketLoggingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)清单信息
+ 'GetBucketInventory' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?inventory',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketInventoryOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Id' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'id',
+ )
+ ),
+ ),
+ // 获取存储桶(Bucket)标签信息
+ 'GetBucketTagging' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketTaggingOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ )
+ ),
+ ),
+ // 分块上传
+ 'UploadPart' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UploadPartOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'UploadPartRequest'
+ )
+ ),
+ 'parameters' => array(
+ 'Body' => array(
+ 'type' => array(
+ 'any'),
+ 'location' => 'body'
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length'
+ ),
+ 'ContentMD5' => array(
+ 'type' => array(
+ 'boolean'
+ ),
+ 'location' => 'header',
+ 'sentAs' => 'Content-MD5'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'PartNumber' => array(
+ 'required' => true,
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'partNumber'),
+ 'UploadId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'uploadId'),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'TrafficLimit' => array(
+ 'type' => 'integer',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-traffic-limit',
+ )
+ )
+ ),
+ // 上传对象
+ 'PutObject' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutObjectOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'PutObjectRequest'
+ )
+ ),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl'
+ ),
+ 'Body' => array(
+ 'required' => true,
+ 'type' => array(
+ 'any'
+ ),
+ 'location' => 'body'
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control'
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition'
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding'
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language'
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length'
+ ),
+ 'ContentMD5' => array(
+ 'type' => array(
+ 'boolean'
+ ),
+ 'location' => 'header',
+ 'sentAs' => 'Content-MD5'
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-cos-kms-key-id',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ ),
+ 'PicOperations' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Pic-Operations',
+ ),
+ 'TrafficLimit' => array(
+ 'type' => 'integer',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-traffic-limit',
+ ),
+ 'Tagging' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-tagging',
+ ),
+ )
+ ),
+ // 追加对象
+ 'AppendObject' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?append',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'AppendObjectOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'AppendObjectRequest'
+ )
+ ),
+ 'parameters' => array(
+ 'Position' => array(
+ 'type' => 'integer',
+ 'required' => true,
+ 'location' => 'query',
+ 'sentAs' => 'position'
+ ),
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl'
+ ),
+ 'Body' => array(
+ 'required' => true,
+ 'type' => array(
+ 'any'
+ ),
+ 'location' => 'body'
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control'
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition'
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding'
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language'
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length'
+ ),
+ 'ContentMD5' => array(
+ 'type' => array(
+ 'boolean'
+ ),
+ 'location' => 'header',
+ 'sentAs' => 'Content-MD5'
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-cos-kms-key-id',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ ),
+ 'PicOperations' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Pic-Operations',
+ ),
+ 'TrafficLimit' => array(
+ 'type' => 'integer',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-traffic-limit',
+ )
+ )
+ ),
+ // 设置 COS 对象的访问权限信息(Access Control List, ACL)
+ 'PutObjectAcl' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}?acl',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutObjectAclOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'AccessControlPolicy',
+ ),
+ ),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl',
+ ),
+ 'Grants' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'AccessControlList',
+ 'items' => array(
+ 'name' => 'Grant',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Grantee' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'),
+ 'ID' => array(
+ 'type' => 'string'),
+ 'Type' => array(
+ 'type' => 'string',
+ 'sentAs' => 'xsi:type',
+ 'data' => array(
+ 'xmlAttribute' => true,
+ 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance')),
+ 'URI' => array(
+ 'type' => 'string') )),
+ 'Permission' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'GrantFullControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-full-control',
+ ),
+ 'GrantRead' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read',
+ ),
+ 'GrantReadACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read-acp',
+ ),
+ 'GrantWrite' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write',
+ ),
+ 'GrantWriteACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write-acp',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ ),
+ )
+ ),
+ // 设置存储桶(Bucket)的访问权限(Access Control List, ACL)
+ 'PutBucketAcl' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?acl',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketAclOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'AccessControlPolicy',
+ ),
+ ),
+ 'parameters' => array(
+ 'ACL' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-acl',
+ ),
+ 'Grants' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'AccessControlList',
+ 'items' => array(
+ 'name' => 'Grant',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Grantee' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'EmailAddress' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'Type' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'sentAs' => 'xsi:type',
+ 'data' => array(
+ 'xmlAttribute' => true,
+ 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
+ ),
+ ),
+ 'URI' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Permission' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'GrantFullControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-full-control',
+ ),
+ 'GrantRead' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read',
+ ),
+ 'GrantReadACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-read-acp',
+ ),
+ 'GrantWrite' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write',
+ ),
+ 'GrantWriteACP' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-grant-write-acp',
+ ),
+ 'ACP' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ ),
+ ),
+ ),
+ // 设置存储桶(Bucket)的跨域配置信息
+ 'PutBucketCors' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?cors',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketCorsOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CORSConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'CORSRules' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'CORSRule',
+ 'type' => 'object',
+ 'sentAs' => 'CORSRule',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'AllowedHeaders' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'AllowedHeader',
+ 'type' => 'string',
+ 'sentAs' => 'AllowedHeader',
+ ),
+ ),
+ 'AllowedMethods' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'AllowedMethod',
+ 'type' => 'string',
+ 'sentAs' => 'AllowedMethod',
+ ),
+ ),
+ 'AllowedOrigins' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'AllowedOrigin',
+ 'type' => 'string',
+ 'sentAs' => 'AllowedOrigin',
+ ),
+ ),
+ 'ExposeHeaders' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'ExposeHeader',
+ 'type' => 'string',
+ 'sentAs' => 'ExposeHeader',
+ ),
+ ),
+ 'MaxAgeSeconds' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 设置存储桶(Bucket)的Domain信息
+ 'PutBucketDomain' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?domain',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketDomainOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'DomainConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'DomainRules' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'DomainRule',
+ 'type' => 'object',
+ 'sentAs' => 'DomainRule',
+ 'properties' => array(
+ 'Status' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Name' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Type' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'ForcedReplacement' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 设置存储桶(Bucket)生命周期配置
+ 'PutBucketLifecycle' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?lifecycle',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketLifecycleOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'LifecycleConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Rules' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Rule',
+ 'type' => 'object',
+ 'sentAs' => 'Rule',
+ 'properties' => array(
+ 'Expiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Date' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time',
+ ),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'Filter' => array(
+ 'type' => 'object',
+ 'require' => true,
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'require' => true,
+ ),
+ 'Tag' => array(
+ 'type' => 'object',
+ 'require' => true,
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string'
+ ),
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'),
+ 'Value' => array(
+ 'type' => 'string'
+ ),
+ )
+ )
+ ),
+ ),
+ 'Status' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Transitions' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Transition',
+ 'type' => 'object',
+ 'sentAs' => 'Transition',
+ 'properties' => array(
+ 'Date' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time',
+ ),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ )))),
+ 'NoncurrentVersionTransition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'NoncurrentVersionExpiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 存储桶(Bucket)版本控制
+ 'PutBucketVersioning' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?versioning',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketVersioningOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'VersioningConfiguration',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'MFA' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-mfa',
+ ),
+ 'MFADelete' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ 'sentAs' => 'MfaDelete',
+ ),
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ // 配置存储桶(Bucket)Accelerate
+ 'PutBucketAccelerate' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?accelerate',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketAccelerateOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'AccelerateConfiguration',
+ ),
+ 'xmlAllowEmpty' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Status' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ )
+ ),
+ ),
+ // 配置存储桶(Bucket)website
+ 'PutBucketWebsite' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?website',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketWebsiteOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'WebsiteConfiguration',
+ ),
+ 'xmlAllowEmpty' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'ErrorDocument' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ 'minLength' => 1,
+ ),
+ ),
+ ),
+ 'IndexDocument' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Suffix' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'RedirectAllRequestsTo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HostName' => array(
+ 'type' => 'string',
+ ),
+ 'Protocol' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'RoutingRules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'RoutingRule',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Condition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'HttpErrorCodeReturnedEquals' => array(
+ 'type' => 'string',
+ ),
+ 'KeyPrefixEquals' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Redirect' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'HostName' => array(
+ 'type' => 'string',
+ ),
+ 'HttpRedirectCode' => array(
+ 'type' => 'string',
+ ),
+ 'Protocol' => array(
+ 'type' => 'string',
+ ),
+ 'ReplaceKeyPrefixWith' => array(
+ 'type' => 'string',
+ ),
+ 'ReplaceKeyWith' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 配置存储桶(Bucket)跨区域复制
+ 'PutBucketReplication' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?replication',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketReplicationOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'ReplicationConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Role' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Rules' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'ReplicationRule',
+ 'type' => 'object',
+ 'sentAs' => 'Rule',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'Prefix' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Status' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Destination' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'properties' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 设置存储桶(Bucket)的回调设置
+ 'PutBucketNotification' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?notification',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketNotificationOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'NotificationConfiguration',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'CloudFunctionConfigurations' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'CloudFunctionConfiguration',
+ 'type' => 'object',
+ 'sentAs' => 'CloudFunctionConfiguration',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'CloudFunction' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'sentAs' => 'CloudFunction',
+ ),
+ 'Events' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ 'sentAs' => 'Event',
+ ),
+ ),
+ 'Filter' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'object',
+ 'sentAs' => 'Key',
+ 'properties' => array(
+ 'FilterRules' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'FilterRule',
+ 'type' => 'object',
+ 'sentAs' => 'FilterRule',
+ 'properties' => array(
+ 'Name' => array(
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ // 配置存储桶(Bucket)标签
+ 'PutBucketTagging' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?tagging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketTaggingOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Tagging',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'TagSet' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'TagRule',
+ 'type' => 'object',
+ 'sentAs' => 'Tag',
+ 'properties' => array(
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ //开启存储桶(Bucket)日志服务
+ 'PutBucketLogging' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?logging',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketLoggingOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'BucketLoggingStatus',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'LoggingEnabled' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TargetBucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'TargetPrefix' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ )
+ ),
+ ),
+ ),
+ // 配置存储桶(Bucket)清单
+ 'PutBucketInventory' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?inventory',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketInventoryOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'InventoryConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Id' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'IsEnabled' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Destination' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'COSBucketDestination'=> array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Format' => array(
+ 'type' => 'string',
+ 'require' => true,
+ ),
+ 'AccountId' => array(
+ 'type' => 'string',
+ 'require' => true,
+ ),
+ 'Bucket' => array(
+ 'type' => 'string',
+ 'require' => true,
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ 'Encryption' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'SSE-COS' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'Schedule' => array(
+ 'required' => true,
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Frequency' => array(
+ 'type' => 'string',
+ 'require' => true,
+ ),
+ )
+ ),
+ 'Filter' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ )
+ ),
+ 'IncludedObjectVersions' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'OptionalFields' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'Fields',
+ 'type' => 'string',
+ 'sentAs' => 'Field',
+ ),
+ ),
+ ),
+ ),
+ // 回热归档对象
+ 'RestoreObject' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?restore',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'RestoreObjectOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'RestoreRequest',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
+ 'Days' => array(
+ 'required' => true,
+ 'type' => 'numeric',
+ 'location' => 'xml',
+ ),
+ 'CASJobParameters' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Tier' => array(
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ ),
+ ),
+ // 查询存储桶(Bucket)中正在进行中的分块上传对象
+ 'ListParts' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListPartsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'MaxParts' => array(
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'max-parts'),
+ 'PartNumberMarker' => array(
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'part-number-marker'
+ ),
+ 'UploadId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'uploadId'
+ )
+ )
+ ),
+ // 查询存储桶(Bucket)下的部分或者全部对象
+ 'ListObjects' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListObjectsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'delimiter'
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type'
+ ),
+ 'Marker' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'marker'
+ ),
+ 'MaxKeys' => array(
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'max-keys'
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'prefix'
+ )
+ )
+ ),
+ // 获取所属账户的所有存储空间列表
+ 'ListBuckets' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListBucketsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ ),
+ ),
+ // 获取多版本对象
+ 'ListObjectVersions' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?versions',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListObjectVersionsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'delimiter',
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type',
+ ),
+ 'KeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'key-marker',
+ ),
+ 'MaxKeys' => array(
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'max-keys',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'prefix',
+ ),
+ 'VersionIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'version-id-marker',
+ )
+ ),
+ ),
+ // 获取已上传分块列表
+ 'ListMultipartUploads' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?uploads',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListMultipartUploadsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'delimiter',
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type',
+ ),
+ 'KeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'key-marker',
+ ),
+ 'MaxUploads' => array(
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'max-uploads',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'prefix',
+ ),
+ 'UploadIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'upload-id-marker',
+ )
+ ),
+ ),
+ // 获取清单列表
+ 'ListBucketInventoryConfigurations' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?inventory',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ListBucketInventoryConfigurationsOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ ),
+ 'ContinuationToken' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'continuation-token',
+ ),
+ ),
+ ),
+ // 获取对象的meta信息
+ 'HeadObject' => array(
+ 'httpMethod' => 'HEAD',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'HeadObjectOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'IfMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'If-Match',
+ ),
+ 'IfModifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'If-Modified-Since',
+ ),
+ 'IfNoneMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'If-None-Match',
+ ),
+ 'IfUnmodifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'If-Unmodified-Since',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'Range' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ ),
+ )
+ ),
+ // 存储桶(Bucket)是否存在
+ 'HeadBucket' => array(
+ 'httpMethod' => 'HEAD',
+ 'uri' => '/{Bucket}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'HeadBucketOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ )
+ ),
+ // 分块copy
+ 'UploadPartCopy' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'UploadPartCopyOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'UploadPartCopyRequest',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'CopySource' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source',
+ ),
+ 'CopySourceIfMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-match',
+ ),
+ 'CopySourceIfModifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-modified-since',
+ ),
+ 'CopySourceIfNoneMatch' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-none-match',
+ ),
+ 'CopySourceIfUnmodifiedSince' => array(
+ 'type' => array(
+ 'object',
+ 'string',
+ 'integer',
+ ),
+ 'format' => 'date-time-http',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-if-unmodified-since',
+ ),
+ 'CopySourceRange' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-range',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'PartNumber' => array(
+ 'required' => true,
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'partNumber',
+ ),
+ 'UploadId' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'uploadId',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'CopySourceSSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-algorithm',
+ ),
+ 'CopySourceSSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key',
+ ),
+ 'CopySourceSSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-server-side-encryption-customer-key-MD5',
+ ),
+ 'RequestPayer' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-payer',
+ )
+ ),
+ ),
+ // 检索对象内容
+ 'SelectObjectContent' => array(
+ 'httpMethod' => 'Post',
+ 'uri' => '/{/Key*}?select&select-type=2',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'SelectObjectContentOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'SelectRequest',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey')
+ ),
+ 'Expression' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'ExpressionType' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'InputSerialization' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'CompressionType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'CSV' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'FileHeaderInfo' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RecordDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'FieldDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'QuoteCharacter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'QuoteEscapeCharacter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Comments' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'AllowQuotedRecordDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ )
+ ),
+ 'JSON' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Type' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ )
+ )
+ ),
+ )
+ ),
+ 'OutputSerialization' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'CompressionType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'CSV' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'QuoteFields' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RecordDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'FieldDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'QuoteCharacter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'QuoteEscapeCharacter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ )
+ ),
+ 'JSON' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'RecordDelimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ )
+ )
+ ),
+ )
+ ),
+ 'RequestProgress' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Enabled' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ )
+ ),
+ ),
+ ),
+ // 存储桶(Bucket)开启智能分层
+ 'PutBucketIntelligentTiering' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?intelligenttiering',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketIntelligentTieringOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'IntelligentTieringConfiguration',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Transition' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Days' => array(
+ 'type' => 'integer',
+ 'location' => 'xml',
+ ),
+ 'RequestFrequent' => array(
+ 'type' => 'integer',
+ 'location' => 'xml',
+ ),
+ )
+ ),
+ ),
+ ),
+ // 查询存储桶(Bucket)智能分层
+ 'GetBucketIntelligentTiering' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?intelligenttiering',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketIntelligentTieringOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //万象-获取图片基本信息
+ 'ImageInfo' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?imageInfo',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageInfoOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ )
+ ),
+ //万象-获取图片EXIF信息
+ 'ImageExif' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?exif',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageExifOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ )
+ ),
+ //万象-获取图片主色调信息
+ 'ImageAve' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?imageAve',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageAveOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ ),
+ ),
+ //万象-云上数据处理
+ 'ImageProcess' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}{/Key*}?image_process',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'ImageProcessOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'PicOperations' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Pic-Operations',
+ ),
+ ),
+ ),
+ //万象-二维码下载时识别
+ 'Qrcode' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=QRcode',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'QrcodeOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'Cover' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'cover'
+ ),
+ ),
+ ),
+ //万象-二维码生成
+ 'QrcodeGenerate' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?ci-process=qrcode-generate',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'QrcodeGenerateOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'QrcodeContent' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'qrcode-content'
+ ),
+ 'QrcodeMode' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'mode'
+ ),
+ 'QrcodeWidth' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'width'
+ ),
+ ),
+ ),
+ //万象-图片标签
+ 'DetectLabel' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}?ci-process=detect-label',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectLabelOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ ),
+ ),
+ //万象-增加样式
+ 'PutBucketImageStyle' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?style',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketImageStyleOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'AddStyle',
+ ),
+ ),
+ 'parameters' => array(
+ 'StyleName' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'StyleBody' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //万象-查询样式
+ 'GetBucketImageStyle' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?style',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketImageStyleOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'GetStyle',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'StyleName' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ //万象-删除样式
+ 'DeleteBucketImageStyle' => array(
+ 'httpMethod' => 'Delete',
+ 'uri' => '/{Bucket}?style',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketImageStyleOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'DeleteStyle',
+ ),
+ ),
+ 'parameters' => array(
+ 'StyleName' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //万象-开通Guetzli压缩
+ 'PutBucketGuetzli' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?guetzli',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketGuetzliOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //万象-查询Guetzli状态
+ 'GetBucketGuetzli' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?guetzli',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketGuetzliOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //万象-关闭Guetzli压缩
+ 'DeleteBucketGuetzli' => array(
+ 'httpMethod' => 'Delete',
+ 'uri' => '/{Bucket}?guetzli',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DeleteBucketGuetzliOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ ),
+ ),
+ //图片审核
+ 'GetObjectSensitiveContentRecognition' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetObjectSensitiveContentRecognitionOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'DetectType' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'detect-type'
+ ),
+ 'DetectUrl' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'detect-url'
+ ),
+ 'Interval' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'interval'
+ ),
+ 'MaxFrames' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'max-frames'
+ ),
+ 'BizType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'biz-type'
+ )
+ ),
+ ),
+ // 文本审核
+ 'DetectText' => array(
+ 'httpMethod' => 'POST',
+ 'uri' => '/{Bucket}text/auditing',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'DetectTextOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'Request',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Input' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Content' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Object' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Url' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'DataId' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'UserInfo' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ 'Conf' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'DetectType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'Callback' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'BizType' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackVersion' => array( 'type' => 'string', 'location' => 'xml', ),
+ 'CallbackType' => array( 'type' => 'integer', 'location' => 'xml', ),
+ ),
+ ),
+ ),
+ ),
+ //媒体截图
+ 'GetSnapshot' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetSnapshotOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'Time' => array(
+ 'required' => true,
+ 'type' => 'numeric',
+ 'location' => 'query',
+ 'sentAs' => 'time'
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ ),
+ 'Width' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'width'
+ ),
+ 'Height' => array(
+ 'type' => 'integer',
+ 'location' => 'query',
+ 'sentAs' => 'height'
+ ),
+ 'Format' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'format'
+ ),
+ 'Rotate' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'rotate'
+ ),
+ 'Mode' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'mode'
+ )
+ ),
+ ),
+ //添加防盗链
+ 'PutBucketReferer' => array(
+ 'httpMethod' => 'PUT',
+ 'uri' => '/{Bucket}?referer',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'PutBucketRefererOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'RefererConfiguration',
+ ),
+ 'contentMd5' => true,
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Status' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RefererType' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'EmptyReferConfiguration' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+
+ 'DomainList' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Domains' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Domain',
+ 'type' => 'string',
+ 'sentAs' => 'Domain',
+ ),
+ )
+ )
+ ),
+ ),
+ ),
+ //获取防盗链规则
+ 'GetBucketReferer' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}?referer',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetBucketRefererOutput',
+ 'responseType' => 'model',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'RefererConfiguration',
+ ),
+ ),
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri'
+ )
+ )
+ ),
+ //获取媒体信息
+ 'GetMediaInfo' => array(
+ 'httpMethod' => 'GET',
+ 'uri' => '/{Bucket}{/Key*}',
+ 'class' => 'Qcloud\\Cos\\Command',
+ 'responseClass' => 'GetMediaInfoOutput',
+ 'responseType' => 'model',
+ 'parameters' => array(
+ 'Bucket' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ ),
+ 'Key' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'uri',
+ 'minLength' => 1,
+ 'filters' => array(
+ 'Qcloud\\Cos\\Client::explodeKey'
+ )
+ ),
+ 'ci-process' => array(
+ 'required' => true,
+ 'type' => 'string',
+ 'location' => 'query'
+ )
+ ),
+ ),
+ 'CreateMediaTranscodeJobs' => Descriptions::CreateMediaTranscodeJobs(), // 媒体转码
+ 'CreateMediaJobs' => Descriptions::CreateMediaJobs(), // 媒体任务
+ 'DescribeMediaJob' => Descriptions::DescribeMediaJob(), // 查询指定的媒体任务
+ 'DescribeMediaJobs' => Descriptions::DescribeMediaJobs(), // 拉取拉取符合条件的媒体任务
+ 'CreateMediaSnapshotJobs' => Descriptions::CreateMediaSnapshotJobs(), // 媒体截图
+ 'CreateMediaConcatJobs' => Descriptions::CreateMediaConcatJobs(), // 媒体拼接
+ 'DetectAudio' => Descriptions::DetectAudio(), // 音频审核
+ 'GetDetectAudioResult' => Descriptions::GetDetectAudioResult(), // 主动获取音频审核结果
+ 'GetDetectTextResult' => Descriptions::GetDetectTextResult(), // 主动获取文本文件审核结果
+ 'DetectVideo' => Descriptions::DetectVideo(), // 视频审核
+ 'GetDetectVideoResult' => Descriptions::GetDetectVideoResult(), // 主动获取视频审核结果
+ 'DetectDocument' => Descriptions::DetectDocument(), // 文档审核
+ 'GetDetectDocumentResult' => Descriptions::GetDetectDocumentResult(), // 主动获取文档审核结果
+ 'CreateDocProcessJobs' => Descriptions::CreateDocProcessJobs(), // 提交文档转码任务
+ 'DescribeDocProcessQueues' => Descriptions::DescribeDocProcessQueues(), // 查询文档转码队列
+ 'DescribeDocProcessJob' => Descriptions::DescribeDocProcessJob(), // 查询文档转码任务
+ 'GetDescribeDocProcessJobs' => Descriptions::GetDescribeDocProcessJobs(), // 拉取符合条件的文档转码任务
+ 'DetectImage' => Descriptions::DetectImage(), // 图片审核
+ 'DetectImages' => Descriptions::DetectImages(), // 图片审核-批量
+ 'DetectVirus' => Descriptions::DetectVirus(), // 云查毒
+ 'GetDetectVirusResult' => Descriptions::GetDetectVirusResult(), // 查询病毒检测任务结果
+ 'GetDetectImageResult' => Descriptions::GetDetectImageResult(), // 主动获取图片审核结果
+ 'CreateMediaVoiceSeparateJobs' => Descriptions::CreateMediaVoiceSeparateJobs(), // 提交人声分离任务
+ 'DescribeMediaVoiceSeparateJob' => Descriptions::DescribeMediaVoiceSeparateJob(), // 查询指定的人声分离任务
+ 'DetectWebpage' => Descriptions::DetectWebpage(), // 提交网页审核任务
+ 'GetDetectWebpageResult' => Descriptions::GetDetectWebpageResult(), // 查询网页审核任务结果
+ 'DescribeMediaBuckets' => Descriptions::DescribeMediaBuckets(), // 查询媒体处理开通状态
+ 'GetPrivateM3U8' => Descriptions::GetPrivateM3U8(), // 获取私有 M3U8 ts 资源的下载授权
+ 'DescribeMediaQueues' => Descriptions::DescribeMediaQueues(), // 搜索媒体处理队列
+ 'UpdateMediaQueue' => Descriptions::UpdateMediaQueue(), // 更新媒体处理队列
+ 'CreateMediaSmartCoverJobs' => Descriptions::CreateMediaSmartCoverJobs(), // 提交智能封面任务
+ 'CreateMediaVideoProcessJobs' => Descriptions::CreateMediaVideoProcessJobs(), // 提交视频增强任务
+ 'CreateMediaVideoMontageJobs' => Descriptions::CreateMediaVideoMontageJobs(), // 提交精彩集锦任务
+ 'CreateMediaAnimationJobs' => Descriptions::CreateMediaAnimationJobs(), // 提交动图任务
+ 'CreateMediaPicProcessJobs' => Descriptions::CreateMediaPicProcessJobs(), // 提交图片处理任务
+ 'CreateMediaSegmentJobs' => Descriptions::CreateMediaSegmentJobs(), // 提交转封装任务
+ 'CreateMediaVideoTagJobs' => Descriptions::CreateMediaVideoTagJobs(), // 提交视频标签任务
+ 'CreateMediaSuperResolutionJobs' => Descriptions::CreateMediaSuperResolutionJobs(), // 提交超分辨率任务
+ 'CreateMediaSDRtoHDRJobs' => Descriptions::CreateMediaSDRtoHDRJobs(), // 提交 SDR to HDR 任务
+ 'CreateMediaDigitalWatermarkJobs' => Descriptions::CreateMediaDigitalWatermarkJobs(), // 嵌入数字水印任务(添加水印)
+ 'CreateMediaExtractDigitalWatermarkJobs' => Descriptions::CreateMediaExtractDigitalWatermarkJobs(), // 提取数字水印任务(提取水印)
+ 'DetectLiveVideo' => Descriptions::DetectLiveVideo(), // 直播流审核
+ 'CancelLiveVideoAuditing' => Descriptions::CancelLiveVideoAuditing(), // 取消直播流审核
+ 'OpticalOcrRecognition' => Descriptions::OpticalOcrRecognition(), // 通用文字识别
+ 'TriggerWorkflow' => Descriptions::TriggerWorkflow(), // 手动触发工作流
+ 'GetWorkflowInstances' => Descriptions::GetWorkflowInstances(), // 获取工作流实例列表
+ 'GetWorkflowInstance' => Descriptions::GetWorkflowInstance(), // 获取工作流实例详情
+ 'CreateMediaSnapshotTemplate' => Descriptions::CreateMediaSnapshotTemplate(), // 新增截图模板
+ 'UpdateMediaSnapshotTemplate' => Descriptions::UpdateMediaSnapshotTemplate(), // 更新截图模板
+ 'CreateMediaTranscodeTemplate' => Descriptions::CreateMediaTranscodeTemplate(), // 新增转码模板
+ 'UpdateMediaTranscodeTemplate' => Descriptions::UpdateMediaTranscodeTemplate(), // 更新转码模板
+ 'CreateMediaHighSpeedHdTemplate' => Descriptions::CreateMediaHighSpeedHdTemplate(), // 新增极速高清转码模板
+ 'UpdateMediaHighSpeedHdTemplate' => Descriptions::UpdateMediaHighSpeedHdTemplate(), // 更新极速高清转码模板
+ 'CreateMediaAnimationTemplate' => Descriptions::CreateMediaAnimationTemplate(), // 新增动图模板
+ 'UpdateMediaAnimationTemplate' => Descriptions::UpdateMediaAnimationTemplate(), // 更新动图模板
+ 'CreateMediaConcatTemplate' => Descriptions::CreateMediaConcatTemplate(), // 新增拼接模板
+ 'UpdateMediaConcatTemplate' => Descriptions::UpdateMediaConcatTemplate(), // 更新拼接模板
+ 'CreateMediaVideoProcessTemplate' => Descriptions::CreateMediaVideoProcessTemplate(), // 新增视频增强模板
+ 'UpdateMediaVideoProcessTemplate' => Descriptions::UpdateMediaVideoProcessTemplate(), // 更新视频增强模板
+ 'CreateMediaVideoMontageTemplate' => Descriptions::CreateMediaVideoMontageTemplate(), // 新增精彩集锦模板
+ 'UpdateMediaVideoMontageTemplate' => Descriptions::UpdateMediaVideoMontageTemplate(), // 更新精彩集锦模板
+ 'CreateMediaVoiceSeparateTemplate' => Descriptions::CreateMediaVoiceSeparateTemplate(), // 新增人声分离模板
+ 'UpdateMediaVoiceSeparateTemplate' => Descriptions::UpdateMediaVoiceSeparateTemplate(), // 更新人声分离模板
+ 'CreateMediaSuperResolutionTemplate' => Descriptions::CreateMediaSuperResolutionTemplate(), // 新增超分辨率模板
+ 'UpdateMediaSuperResolutionTemplate' => Descriptions::UpdateMediaSuperResolutionTemplate(), // 更新超分辨率模板
+ 'CreateMediaPicProcessTemplate' => Descriptions::CreateMediaPicProcessTemplate(), // 新增图片处理模板
+ 'UpdateMediaPicProcessTemplate' => Descriptions::UpdateMediaPicProcessTemplate(), // 更新图片处理模板
+ 'CreateMediaWatermarkTemplate' => Descriptions::CreateMediaWatermarkTemplate(), // 新增水印模板
+ 'UpdateMediaWatermarkTemplate' => Descriptions::UpdateMediaWatermarkTemplate(), // 更新水印模板
+ 'DescribeMediaTemplates' => Descriptions::DescribeMediaTemplates(), // 查询模板列表
+ 'DescribeWorkflow' => Descriptions::DescribeWorkflow(), // 搜索工作流
+ 'DeleteWorkflow' => Descriptions::DeleteWorkflow(), // 删除工作流
+ 'CreateInventoryTriggerJob' => Descriptions::CreateInventoryTriggerJob(), // 触发批量存量任务
+ 'DescribeInventoryTriggerJobs' => Descriptions::DescribeInventoryTriggerJobs(), // 批量拉取存量任务
+ 'DescribeInventoryTriggerJob' => Descriptions::DescribeInventoryTriggerJob(), // 查询存量任务
+ 'CancelInventoryTriggerJob' => Descriptions::CancelInventoryTriggerJob(), // 取消存量任务
+ 'CreateMediaNoiseReductionJobs' => Descriptions::CreateMediaNoiseReductionJobs(), // 提交音频降噪任务
+ 'ImageRepairProcess' => Descriptions::ImageRepairProcess(), // 图片水印修复
+ 'ImageDetectCarProcess' => Descriptions::ImageDetectCarProcess(), // 车辆车牌检测
+ 'ImageAssessQualityProcess' => Descriptions::ImageAssessQualityProcess(), // 图片质量评估
+ 'ImageSearchOpen' => Descriptions::ImageSearchOpen(), // 开通以图搜图
+ 'ImageSearchAdd' => Descriptions::ImageSearchAdd(), // 添加图库图片
+ 'ImageSearch' => Descriptions::ImageSearch(), // 图片搜索接口
+ 'ImageSearchDelete' => Descriptions::ImageSearchDelete(), // 图片搜索接口
+ 'BindCiService' => Descriptions::BindCiService(), // 绑定数据万象服务
+ 'GetCiService' => Descriptions::GetCiService(), // 查询数据万象服务
+ 'UnBindCiService' => Descriptions::UnBindCiService(), // 解绑数据万象服务
+ 'GetHotLink' => Descriptions::GetHotLink(), // 查询防盗链
+ 'AddHotLink' => Descriptions::AddHotLink(), // 查询防盗链
+ 'OpenOriginProtect' => Descriptions::OpenOriginProtect(), // 开通原图保护
+ 'GetOriginProtect' => Descriptions::GetOriginProtect(), // 查询原图保护状态
+ 'CloseOriginProtect' => Descriptions::CloseOriginProtect(), // 关闭原图保护
+ 'ImageDetectFace' => Descriptions::ImageDetectFace(), // 人脸检测
+ 'ImageFaceEffect' => Descriptions::ImageFaceEffect(), // 人脸特效
+ 'IDCardOCR' => Descriptions::IDCardOCR(), // 身份证识别
+ 'IDCardOCRByUpload' => Descriptions::IDCardOCRByUpload(), // 身份证识别-上传时处理
+ 'GetLiveCode' => Descriptions::GetLiveCode(), // 获取数字验证码
+ 'GetActionSequence' => Descriptions::GetActionSequence(), // 获取动作顺序
+ 'DescribeDocProcessBuckets' => Descriptions::DescribeDocProcessBuckets(), // 查询文档预览开通状态
+ 'UpdateDocProcessQueue' => Descriptions::UpdateDocProcessQueue(), // 更新文档转码队列
+ 'CreateMediaQualityEstimateJobs' => Descriptions::CreateMediaQualityEstimateJobs(), // 提交视频质量评分任务
+ 'CreateMediaStreamExtractJobs' => Descriptions::CreateMediaStreamExtractJobs(), // 提交音视频流分离任务
+ ),
+ 'models' => array(
+ 'AbortMultipartUploadOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'CreateBucketOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Location' => array(
+ 'type' => 'string',
+ 'location' => 'header'
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'CompleteMultipartUploadOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Location' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ ),
+ 'ImageInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'Width' => array(
+ 'type' => 'string',
+ ),
+ 'Height' => array(
+ 'type' => 'string',
+ ),
+ 'Quality' => array(
+ 'type' => 'string',
+ ),
+ 'Ave' => array(
+ 'type' => 'string',
+ ),
+ 'Orientation' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'ProcessResults' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array(
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Location' => array(
+ 'type' => 'string',
+ ),
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'Width' => array(
+ 'type' => 'string',
+ ),
+ 'Height' => array(
+ 'type' => 'string',
+ ),
+ 'Size' => array(
+ 'type' => 'string',
+ ),
+ 'Quality' => array(
+ 'type' => 'string',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ ),
+ 'WatermarkStatus' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'CreateMultipartUploadOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Bucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ 'sentAs' => 'Bucket'
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'UploadId' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ )
+ )
+ ),
+ 'CopyObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'CopySourceVersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-version-id',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ ),
+ ),
+ 'DeleteBucketOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'DeleteBucketCorsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteBucketTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteBucketInventoryOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'DeleteMarker' => array(
+ 'type' => 'boolean',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-delete-marker',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteObjectsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Deleted' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Deleted',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ ),
+ 'DeleteMarker' => array(
+ 'type' => 'boolean',
+ ),
+ 'DeleteMarkerVersionId' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'Errors' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Error',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ ),
+ 'Code' => array(
+ 'type' => 'string',
+ ),
+ 'Message' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteBucketLifecycleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteBucketReplicationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteBucketWebsiteOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutObjectTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetObjectTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'TagSet' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'sentAs' => 'Tag',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'DeleteObjectTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'GetObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'DeleteMarker' => array(
+ 'type' => 'boolean',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-delete-marker',
+ ),
+ 'AcceptRanges' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'accept-ranges',
+ ),
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'Restore' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-restore',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Last-Modified',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'MissingMeta' => array(
+ 'type' => 'numeric',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-missing-meta',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control',
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition',
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding',
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language',
+ ),
+ 'ContentRange' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Range',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'Expires' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'ReplicationStatus' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-replication-status',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ ),
+ ),
+ 'GetObjectAclOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Grants' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'AccessControlList',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Grantee' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'),
+ 'ID' => array(
+ 'type' => 'string'))),
+ 'Permission' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketAclOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'
+ ),
+ 'ID' => array(
+ 'type' => 'string'
+ )
+ )
+ ),
+ 'Grants' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'AccessControlList',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Grantee' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'
+ ),
+ 'ID' => array(
+ 'type' => 'string'
+ )
+ )
+ ),
+ 'Permission' => array(
+ 'type' => 'string'
+ )
+ )
+ )
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'GetBucketCorsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'CORSRules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'CORSRule',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string'),
+ 'AllowedHeaders' => array(
+ 'type' => 'array',
+ 'sentAs' => 'AllowedHeader',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'string',
+ )
+ ),
+ 'AllowedMethods' => array(
+ 'type' => 'array',
+ 'sentAs' => 'AllowedMethod',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'AllowedOrigins' => array(
+ 'type' => 'array',
+ 'sentAs' => 'AllowedOrigin',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'ExposeHeaders' => array(
+ 'type' => 'array',
+ 'sentAs' => 'ExposeHeader',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'MaxAgeSeconds' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketDomainOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'DomainRules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'DomainRule',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Status' => array(
+ 'type' => 'string'
+ ),
+ 'Name' => array(
+ 'type' => 'string'
+ ),
+ 'Type' => array(
+ 'type' => 'string'
+ ),
+ 'ForcedReplacement' => array(
+ 'type' => 'string'
+ ),
+ ),
+ ),
+ ),
+ 'DomainTxtVerification' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-domain-txt-verification',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketLifecycleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Rules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Rule',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Expiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Date' => array(
+ 'type' => 'string',
+ ),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'Filter' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ 'Tag' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string'
+ ),
+ 'Value' => array(
+ 'type' => 'string'
+ ),
+ )
+ )
+ ),
+ ),
+ 'Status' => array(
+ 'type' => 'string',
+ ),
+ 'Transition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Date' => array(
+ 'type' => 'string',
+ ),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'NoncurrentVersionTransition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'NoncurrentVersionExpiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketVersioningOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'MFADelete' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ 'sentAs' => 'MfaDelete',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketReplicationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Role' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Rules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Rule',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ 'Status' => array(
+ 'type' => 'string',
+ ),
+ 'Destination' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Bucket' => array(
+ 'type' => 'string',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketLocationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Location' => array(
+ 'type' => 'string',
+ 'location' => 'body',
+ 'filters' => array(
+ 'strval',
+ 'strip_tags',
+ 'trim',
+ ),
+ ),
+ ),
+ ),
+ 'GetBucketAccelerateOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Type' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketWebsiteOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RedirectAllRequestsTo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HostName' => array(
+ 'type' => 'string',
+ ),
+ 'Protocol' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'IndexDocument' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Suffix' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'ErrorDocument' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'RoutingRules' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'RoutingRule',
+ 'type' => 'object',
+ 'sentAs' => 'RoutingRule',
+ 'properties' => array(
+ 'Condition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'HttpErrorCodeReturnedEquals' => array(
+ 'type' => 'string',
+ ),
+ 'KeyPrefixEquals' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Redirect' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'HostName' => array(
+ 'type' => 'string',
+ ),
+ 'HttpRedirectCode' => array(
+ 'type' => 'string',
+ ),
+ 'Protocol' => array(
+ 'type' => 'string',
+ ),
+ 'ReplaceKeyPrefixWith' => array(
+ 'type' => 'string',
+ ),
+ 'ReplaceKeyWith' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketInventoryOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Destination' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'COSBucketDestination' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'AccountId' => array(
+ 'type' => 'string',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ 'Encryption' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'SSE-COS' => array(
+ 'type' => 'string',
+ )
+ )
+ ),
+
+ ),
+ ),
+ ),
+ ),
+ 'Schedule' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Frequency' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'OptionalFields' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'OptionalFields' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'Field',
+ 'type' => 'string',
+ 'sentAs' => 'Field',
+ ),
+ ),
+ 'IsEnabled' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Id' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'IncludedObjectVersions' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'TagSet' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'sentAs' => 'Tag',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketNotificationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'CloudFunctionConfigurations' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'CloudFunctionConfiguration',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'CloudFunction' => array(
+ 'type' => 'string',
+ 'sentAs' => 'CloudFunction',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'sentAs' => 'Event',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'Filter' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'object',
+ 'sentAs' => 'Key',
+ 'properties' => array(
+ 'FilterRules' => array(
+ 'type' => 'array',
+ 'sentAs' => 'FilterRule',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Name' => array(
+ 'type' => 'string',
+ ),
+ 'Value' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketLoggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'LoggingEnabled' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'TargetBucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'TargetPrefix' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'UploadPartOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ ),
+ ),
+ 'UploadPartCopyOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'CopySourceVersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-copy-source-version-id',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ ),
+ ),
+ 'PutBucketAclOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'PutObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ ),
+ ),
+ 'AppendObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'Position' => array(
+ 'type' => 'integer',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-next-append-position',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ )
+ ),
+ ),
+ 'PutObjectAclOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketCorsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketDomainOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketLifecycleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketVersioningOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketReplicationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketNotificationOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketWebsiteOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketAccelerateOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketLoggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketInventoryOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketTaggingOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'RestoreObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'ListPartsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Bucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'UploadId' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'PartNumberMarker' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml'
+ ),
+ 'NextPartNumberMarker' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml'
+ ),
+ 'MaxParts' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml'
+ ),
+ 'IsTruncated' => array(
+ 'type' => 'boolean',
+ 'location' => 'xml'
+ ),
+ 'Parts' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Part',
+ 'data' => array(
+ 'xmlFlattened' => true
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'PartNumber' => array(
+ 'type' => 'numeric'
+ ),
+ 'LastModified' => array(
+ 'type' => 'string'
+ ),
+ 'ETag' => array(
+ 'type' => 'string'
+ ),
+ 'Size' => array(
+ 'type' => 'numeric'
+ )
+ )
+ )
+ ),
+ 'Initiator' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string'
+ ),
+ 'DisplayName' => array(
+ 'type' => 'string'
+ )
+ )
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'
+ ),
+ 'ID' => array(
+ 'type' => 'string'
+ )
+ )
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'ListObjectsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'IsTruncated' => array(
+ 'type' => 'boolean',
+ 'location' => 'xml'
+ ),
+ 'Marker' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'NextMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'Contents' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string'
+ ),
+ 'LastModified' => array(
+ 'type' => 'string'
+ ),
+ 'ETag' => array(
+ 'type' => 'string'
+ ),
+ 'Size' => array(
+ 'type' => 'numeric'
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string'
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string'
+ ),
+ 'ID' => array(
+ 'type' => 'string'
+ )
+ )
+ )
+ )
+ )
+ ),
+ 'Name' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'MaxKeys' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml'
+ ),
+ 'CommonPrefixes' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string'
+ )
+ )
+ )
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml'),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ )
+ )
+ ),
+ 'ListBucketsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Buckets' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Name' => array(
+ 'type' => 'string',
+ ),
+ 'CreationDate' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'ListObjectVersionsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'IsTruncated' => array(
+ 'type' => 'boolean',
+ 'location' => 'xml',
+ ),
+ 'KeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'VersionIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'NextKeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'NextVersionIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Version' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'ETag' => array(
+ 'type' => 'string',
+ ),
+ 'Size' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ ),
+ 'IsLatest' => array(
+ 'type' => 'boolean',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'DeleteMarkers' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'DeleteMarker',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Owner' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ ),
+ 'IsLatest' => array(
+ 'type' => 'boolean',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'Name' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'MaxKeys' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml',
+ ),
+ 'CommonPrefixes' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'ListMultipartUploadsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Bucket' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'KeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'UploadIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'NextKeyMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'NextUploadIdMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'MaxUploads' => array(
+ 'type' => 'numeric',
+ 'location' => 'xml',
+ ),
+ 'IsTruncated' => array(
+ 'type' => 'boolean',
+ 'location' => 'xml',
+ ),
+ 'Uploads' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'Upload',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'UploadId' => array(
+ 'type' => 'string',
+ ),
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Initiated' => array(
+ 'type' => 'string',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ 'Owner' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'Initiator' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'ID' => array(
+ 'type' => 'string',
+ ),
+ 'DisplayName' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'CommonPrefixes' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'ListBucketInventoryConfigurationsOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'InventoryConfiguration' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'sentAs' => 'InventoryConfiguration',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Destination' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'COSBucketDestination' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'AccountId' => array(
+ 'type' => 'string',
+ ),
+ 'Bucket' => array(
+ 'type' => 'string',
+ ),
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ 'Encryption' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'SSE-COS' => array(
+ 'type' => 'string',
+ )
+ )
+ ),
+
+ ),
+ ),
+ ),
+ ),
+ 'Schedule' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Frequency' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'OptionalFields' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'OptionalFields' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'name' => 'Field',
+ 'type' => 'string',
+ 'sentAs' => 'Field',
+ ),
+ ),
+ 'IsEnabled' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Id' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'IncludedObjectVersions' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'HeadObjectOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'DeleteMarker' => array(
+ 'type' => 'boolean',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-delete-marker',
+ ),
+ 'AcceptRanges' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'accept-ranges',
+ ),
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'Restore' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-restore',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Last-Modified',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'MissingMeta' => array(
+ 'type' => 'numeric',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-missing-meta',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control',
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition',
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding',
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'Expires' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'ReplicationStatus' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-replication-status',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CRC' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-hash-crc64ecma',
+ )
+ )
+ ),
+ 'HeadBucketOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'SelectObjectContentOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RawData' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ ),
+ ),
+ 'GetBucketIntelligentTieringOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Transition' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Days' => array(
+ 'type' => 'string',
+ ),
+ 'RequestFrequent' => array(
+ 'type' => 'string',
+ ),
+ )
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketIntelligentTieringOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'ImageInfoOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ ),
+ 'ImageExifOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ ),
+ 'ImageAveOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ ),
+ ),
+ 'ImageProcessOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'OriginalInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Location' => array(
+ 'type' => 'string',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ ),
+ 'ImageInfo' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'Width' => array(
+ 'type' => 'string',
+ ),
+ 'Height' => array(
+ 'type' => 'string',
+ ),
+ 'Quality' => array(
+ 'type' => 'string',
+ ),
+ 'Ave' => array(
+ 'type' => 'string',
+ ),
+ 'Orientation' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ProcessResults' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Object' => array(
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Key' => array(
+ 'type' => 'string',
+ ),
+ 'Location' => array(
+ 'type' => 'string',
+ ),
+ 'Format' => array(
+ 'type' => 'string',
+ ),
+ 'Width' => array(
+ 'type' => 'string',
+ ),
+ 'Height' => array(
+ 'type' => 'string',
+ ),
+ 'Size' => array(
+ 'type' => 'string',
+ ),
+ 'Quality' => array(
+ 'type' => 'string',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ ),
+ 'WatermarkStatus' => array(
+ 'type' => 'integer',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'QrcodeOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'CodeStatus' => array(
+ 'type' => 'integer',
+ 'location' => 'xml',
+ ),
+ 'QRcodeInfo' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'CodeUrl' => array(
+ 'type' => 'string',
+ ),
+ 'Point' => array(
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'ResultImage' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ 'QrcodeGenerateOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ResultImage' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ ),
+ ),
+ 'DetectLabelOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'Labels' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Confidence' => array(
+ 'type' => 'integer',
+ ),
+ 'Name' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PutBucketImageStyleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketImageStyleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'StyleRule' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'StyleName' => array(
+ 'type' => 'string',
+ ),
+ 'StyleBody' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'DeleteBucketImageStyleOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'PutBucketGuetzliOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetBucketGuetzliOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ ),
+ ),
+ 'DeleteBucketGuetzliOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ ),
+ ),
+ 'GetObjectSensitiveContentRecognitionOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'PornInfo' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Code' => array(
+ 'type' => 'integer',
+ ),
+ 'Msg' => array(
+ 'type' => 'string',
+ ),
+ 'HitFlag' => array(
+ 'type' => 'integer',
+ ),
+ 'Score' => array(
+ 'type' => 'integer',
+ ),
+ 'Label' => array(
+ 'type' => 'string',
+ )
+ ),
+ ),
+ ),
+ 'TerroristInfo' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Code' => array(
+ 'type' => 'integer',
+ ),
+ 'Msg' => array(
+ 'type' => 'string',
+ ),
+ 'HitFlag' => array(
+ 'type' => 'integer',
+ ),
+ 'Score' => array(
+ 'type' => 'integer',
+ ),
+ 'Label' => array(
+ 'type' => 'string',
+ )
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Code' => array(
+ 'type' => 'integer',
+ ),
+ 'Msg' => array(
+ 'type' => 'string',
+ ),
+ 'HitFlag' => array(
+ 'type' => 'integer',
+ ),
+ 'Score' => array(
+ 'type' => 'integer',
+ ),
+ 'Label' => array(
+ 'type' => 'string',
+ )
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Code' => array(
+ 'type' => 'integer',
+ ),
+ 'Msg' => array(
+ 'type' => 'string',
+ ),
+ 'HitFlag' => array(
+ 'type' => 'integer',
+ ),
+ 'Score' => array(
+ 'type' => 'integer',
+ ),
+ 'Label' => array(
+ 'type' => 'string',
+ )
+ ),
+ ),
+ ),
+ )
+ ),
+ 'DetectTextOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-ci-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'JobsDetail' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Code' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DataId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Message' => array( 'type' => 'string', 'location' => 'xml',),
+ 'JobId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'State' => array( 'type' => 'string', 'location' => 'xml',),
+ 'CreationTime' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Content' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'SectionCount' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'IllegalInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'AbuseInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Count' => array( 'type' => 'integer', 'location' => 'xml',),
+ ),
+ ),
+ 'Section' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'StartByte' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Label' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Result' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'PornInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'TerrorismInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'PoliticsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AdsInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'IllegalInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'AbuseInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'HitFlag' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Score' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'Keywords' => array( 'type' => 'string', 'location' => 'xml',),
+ 'SubLabel' => array( 'type' => 'string', 'location' => 'xml',),
+ 'LibResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'LibType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'LibName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Keywords' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'UserInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'TokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Nickname' => array( 'type' => 'string', 'location' => 'xml',),
+ 'DeviceId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'AppId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Room' => array( 'type' => 'string', 'location' => 'xml',),
+ 'IP' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Type' => array( 'type' => 'string', 'location' => 'xml',),
+ 'ReceiveTokenId' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Gender' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Level' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Role' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ 'ListInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListResults' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'items' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'ListType' => array( 'type' => 'integer', 'location' => 'xml',),
+ 'ListName' => array( 'type' => 'string', 'location' => 'xml',),
+ 'Entity' => array( 'type' => 'string', 'location' => 'xml',),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ 'GetSnapshotOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'Body' => array(
+ 'type' => 'string',
+ 'instanceOf' => 'GuzzleHttp\\Psr7\\Stream',
+ 'location' => 'body',
+ ),
+ 'DeleteMarker' => array(
+ 'type' => 'boolean',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-delete-marker',
+ ),
+ 'AcceptRanges' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'accept-ranges',
+ ),
+ 'Expiration' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-expiration',
+ ),
+ 'Restore' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-restore',
+ ),
+ 'LastModified' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Last-Modified',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'MissingMeta' => array(
+ 'type' => 'numeric',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-missing-meta',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-version-id',
+ ),
+ 'CacheControl' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Cache-Control',
+ ),
+ 'ContentDisposition' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Disposition',
+ ),
+ 'ContentEncoding' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Encoding',
+ ),
+ 'ContentLanguage' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Language',
+ ),
+ 'ContentRange' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Range',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'Expires' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'WebsiteRedirectLocation' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-website-redirect-location',
+ ),
+ 'ServerSideEncryption' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption',
+ ),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-customer-key-MD5',
+ ),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-server-side-encryption-aws-kms-key-id',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-storage-class',
+ ),
+ 'RequestCharged' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-charged',
+ ),
+ 'ReplicationStatus' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-replication-status',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ )
+ )
+ ),
+ 'PutBucketRefererOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'ETag' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ ),
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ )
+ )
+ ),
+ 'GetBucketRefererOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id'
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'Status' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'RefererType' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'EmptyReferConfiguration' => array(
+ 'type' => 'string',
+ 'location' => 'xml'
+ ),
+ 'DomainList' => array(
+ 'location' => 'xml',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Domains' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Domain',
+ 'type' => 'string',
+ 'sentAs' => 'Domain',
+ ),
+ )
+ )
+ )
+ )
+ ),
+ 'GetMediaInfoOutput' => array(
+ 'type' => 'object',
+ 'additionalProperties' => true,
+ 'properties' => array(
+ 'RequestId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-cos-request-id',
+ ),
+ 'ContentType' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'Content-Type',
+ ),
+ 'ContentLength' => array(
+ 'type' => 'numeric',
+ 'minimum'=> 0,
+ 'location' => 'header',
+ 'sentAs' => 'Content-Length',
+ ),
+ 'MediaInfo' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Stream' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'JobId' => array(
+ 'type' => 'string',
+ ),
+ 'State' => array(
+ 'type' => 'string',
+ ),
+ 'Video' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Index' => array(
+ 'type' => 'integer',
+ ),
+ 'CodecName' => array(
+ 'type' => 'string',
+ ),
+ 'CodecLongName' => array(
+ 'type' => 'string',
+ ),
+ 'CodecTimeBase' => array(
+ 'type' => 'string',
+ ),
+ 'CodecTag' => array(
+ 'type' => 'string',
+ ),
+ 'Profile' => array(
+ 'type' => 'string',
+ ),
+ 'Height' => array(
+ 'type' => 'integer',
+ ),
+ 'Width' => array(
+ 'type' => 'integer',
+ ),
+ 'HasBFrame' => array(
+ 'type' => 'integer',
+ ),
+ 'RefFrames' => array(
+ 'type' => 'integer',
+ ),
+ 'Sar' => array(
+ 'type' => 'string',
+ ),
+ 'Dar' => array(
+ 'type' => 'string',
+ ),
+ 'PixFormat' => array(
+ 'type' => 'string',
+ ),
+ 'FieldOrder' => array(
+ 'type' => 'string',
+ ),
+ 'Level' => array(
+ 'type' => 'integer',
+ ),
+ 'Fps' => array(
+ 'type' => 'integer',
+ ),
+ 'AvgFps' => array(
+ 'type' => 'string',
+ ),
+ 'Timebase' => array(
+ 'type' => 'string',
+ ),
+ 'StartTime' => array(
+ 'type' => 'numeric',
+ ),
+ 'Duration' => array(
+ 'type' => 'numeric',
+ ),
+ 'Bitrate' => array(
+ 'type' => 'numeric',
+ ),
+ 'NumFrames' => array(
+ 'type' => 'integer',
+ ),
+ 'Language' => array(
+ 'type' => 'string',
+ )
+ ),
+ 'Audio' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Index' => array(
+ 'type' => 'integer',
+ ),
+ 'CodecName' => array(
+ 'type' => 'string',
+ ),
+ 'CodecLongName' => array(
+ 'type' => 'string',
+ ),
+ 'CodecTimeBase' => array(
+ 'type' => 'string',
+ ),
+ 'CodecTagString' => array(
+ 'type' => 'string',
+ ),
+ 'CodecTag' => array(
+ 'type' => 'string',
+ ),
+ 'SampleFmt' => array(
+ 'type' => 'string',
+ ),
+ 'SampleRate' => array(
+ 'type' => 'integer',
+ ),
+ 'Channel' => array(
+ 'type' => 'integer',
+ ),
+ 'ChannelLayout' => array(
+ 'type' => 'string',
+ ),
+ 'Timebase' => array(
+ 'type' => 'string',
+ ),
+ 'StartTime' => array(
+ 'type' => 'numeric',
+ ),
+ 'Duration' => array(
+ 'type' => 'numeric',
+ ),
+ 'Bitrate' => array(
+ 'type' => 'numeric',
+ ),
+ 'Language' => array(
+ 'type' => 'string',
+ )
+ )
+ ),
+ 'Subtitle' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Index' => array(
+ 'type' => 'integer',
+ ),
+ 'Language' => array(
+ 'type' => 'string',
+ )
+ )
+ )
+ ),
+ )
+ ),
+ 'Format' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'NumStream' => array(
+ 'type' => 'integer',
+ ),
+ 'NumProgram' => array(
+ 'type' => 'integer',
+ ),
+ 'FormatName' => array(
+ 'type' => 'string',
+ ),
+ 'FormatLongName' => array(
+ 'type' => 'string',
+ ),
+ 'StartTime' => array(
+ 'type' => 'numeric',
+ ),
+ 'Duration' => array(
+ 'type' => 'numeric',
+ ),
+ 'Bitrate' => array(
+ 'type' => 'integer',
+ ),
+ 'Size' => array(
+ 'type' => 'integer',
+ )
+ )
+ )
+ )
+ )
+
+
+ )
+ ),
+ 'CreateMediaTranscodeJobsOutput' => Descriptions::CreateMediaTranscodeJobsOutput(),
+ 'DescribeMediaJobOutput' => Descriptions::DescribeMediaJobOutput(),
+ 'DescribeMediaJobsOutput' => Descriptions::DescribeMediaJobsOutput(),
+ 'CreateMediaJobsOutput' => Descriptions::CreateMediaJobsOutput(),
+ 'CreateMediaSnapshotJobsOutput' => Descriptions::CreateMediaSnapshotJobsOutput(),
+ 'CreateMediaConcatJobsOutput' => Descriptions::CreateMediaConcatJobsOutput(),
+ 'DetectAudioOutput' => Descriptions::DetectAudioOutput(),
+ 'GetDetectAudioResultOutput' => Descriptions::GetDetectAudioResultOutput(),
+ 'GetDetectTextResultOutput' => Descriptions::GetDetectTextResultOutput(),
+ 'DetectVideoOutput' => Descriptions::DetectVideoOutput(),
+ 'GetDetectVideoResultOutput' => Descriptions::GetDetectVideoResultOutput(),
+ 'DetectDocumentOutput' => Descriptions::DetectDocumentOutput(),
+ 'GetDetectDocumentResultOutput' => Descriptions::GetDetectDocumentResultOutput(),
+ 'CreateDocProcessJobsOutput' => Descriptions::CreateDocProcessJobsOutput(),
+ 'DescribeDocProcessQueuesOutput' => Descriptions::DescribeDocProcessQueuesOutput(),
+ 'DescribeDocProcessJobOutput' => Descriptions::DescribeDocProcessJobOutput(),
+ 'GetDescribeDocProcessJobsOutput' => Descriptions::GetDescribeDocProcessJobsOutput(),
+ 'DetectImageOutput' => Descriptions::DetectImageOutput(),
+ 'DetectImagesOutput' => Descriptions::DetectImagesOutput(),
+ 'DetectVirusOutput' => Descriptions::DetectVirusOutput(),
+ 'GetDetectVirusResultOutput' => Descriptions::GetDetectVirusResultOutput(),
+ 'GetDetectImageResultOutput' => Descriptions::GetDetectImageResultOutput(),
+ 'CreateMediaVoiceSeparateJobsOutput' => Descriptions::CreateMediaVoiceSeparateJobsOutput(),
+ 'DescribeMediaVoiceSeparateJobOutput' => Descriptions::DescribeMediaVoiceSeparateJobOutput(),
+ 'DetectWebpageOutput' => Descriptions::DetectWebpageOutput(),
+ 'GetDetectWebpageResultOutput' => Descriptions::GetDetectWebpageResultOutput(),
+ 'DescribeMediaBucketsOutput' => Descriptions::DescribeMediaBucketsOutput(),
+ 'GetPrivateM3U8Output' => Descriptions::GetPrivateM3U8Output(),
+ 'DescribeMediaQueuesOutput' => Descriptions::DescribeMediaQueuesOutput(),
+ 'UpdateMediaQueueOutput' => Descriptions::UpdateMediaQueueOutput(),
+ 'CreateMediaSmartCoverJobsOutput' => Descriptions::CreateMediaSmartCoverJobsOutput(),
+ 'CreateMediaVideoProcessJobsOutput' => Descriptions::CreateMediaVideoProcessJobsOutput(),
+ 'CreateMediaVideoMontageJobsOutput' => Descriptions::CreateMediaVideoMontageJobsOutput(),
+ 'CreateMediaAnimationJobsOutput' => Descriptions::CreateMediaAnimationJobsOutput(),
+ 'CreateMediaPicProcessJobsOutput' => Descriptions::CreateMediaPicProcessJobsOutput(),
+ 'CreateMediaSegmentJobsOutput' => Descriptions::CreateMediaSegmentJobsOutput(),
+ 'CreateMediaVideoTagJobsOutput' => Descriptions::CreateMediaVideoTagJobsOutput(),
+ 'CreateMediaSuperResolutionJobsOutput' => Descriptions::CreateMediaSuperResolutionJobsOutput(),
+ 'CreateMediaSDRtoHDRJobsOutput' => Descriptions::CreateMediaSDRtoHDRJobsOutput(),
+ 'CreateMediaDigitalWatermarkJobsOutput' => Descriptions::CreateMediaDigitalWatermarkJobsOutput(),
+ 'CreateMediaExtractDigitalWatermarkJobsOutput' => Descriptions::CreateMediaExtractDigitalWatermarkJobsOutput(),
+ 'DetectLiveVideoOutput' => Descriptions::DetectLiveVideoOutput(),
+ 'CancelLiveVideoAuditingOutput' => Descriptions::CancelLiveVideoAuditingOutput(),
+ 'OpticalOcrRecognitionOutput' => Descriptions::OpticalOcrRecognitionOutput(),
+ 'TriggerWorkflowOutput' => Descriptions::TriggerWorkflowOutput(),
+ 'GetWorkflowInstancesOutput' => Descriptions::GetWorkflowInstancesOutput(),
+ 'GetWorkflowInstanceOutput' => Descriptions::GetWorkflowInstanceOutput(),
+ 'CreateMediaSnapshotTemplateOutput' => Descriptions::CreateMediaSnapshotTemplateOutput(),
+ 'UpdateMediaSnapshotTemplateOutput' => Descriptions::UpdateMediaSnapshotTemplateOutput(),
+ 'CreateMediaTranscodeTemplateOutput' => Descriptions::CreateMediaTranscodeTemplateOutput(),
+ 'UpdateMediaTranscodeTemplateOutput' => Descriptions::UpdateMediaTranscodeTemplateOutput(),
+ 'CreateMediaHighSpeedHdTemplateOutput' => Descriptions::CreateMediaHighSpeedHdTemplateOutput(),
+ 'UpdateMediaHighSpeedHdTemplateOutput' => Descriptions::UpdateMediaHighSpeedHdTemplateOutput(),
+ 'CreateMediaAnimationTemplateOutput' => Descriptions::CreateMediaAnimationTemplateOutput(),
+ 'UpdateMediaAnimationTemplateOutput' => Descriptions::UpdateMediaAnimationTemplateOutput(),
+ 'CreateMediaConcatTemplateOutput' => Descriptions::CreateMediaConcatTemplateOutput(),
+ 'UpdateMediaConcatTemplateOutput' => Descriptions::UpdateMediaConcatTemplateOutput(),
+ 'CreateMediaVideoProcessTemplateOutput' => Descriptions::CreateMediaVideoProcessTemplateOutput(),
+ 'UpdateMediaVideoProcessTemplateOutput' => Descriptions::UpdateMediaVideoProcessTemplateOutput(),
+ 'CreateMediaVideoMontageTemplateOutput' => Descriptions::CreateMediaVideoMontageTemplateOutput(),
+ 'UpdateMediaVideoMontageTemplateOutput' => Descriptions::UpdateMediaVideoMontageTemplateOutput(),
+ 'CreateMediaVoiceSeparateTemplateOutput' => Descriptions::CreateMediaVoiceSeparateTemplateOutput(),
+ 'UpdateMediaVoiceSeparateTemplateOutput' => Descriptions::UpdateMediaVoiceSeparateTemplateOutput(),
+ 'CreateMediaSuperResolutionTemplateOutput' => Descriptions::CreateMediaSuperResolutionTemplateOutput(),
+ 'UpdateMediaSuperResolutionTemplateOutput' => Descriptions::UpdateMediaSuperResolutionTemplateOutput(),
+ 'CreateMediaPicProcessTemplateOutput' => Descriptions::CreateMediaPicProcessTemplateOutput(),
+ 'UpdateMediaPicProcessTemplateOutput' => Descriptions::UpdateMediaPicProcessTemplateOutput(),
+ 'CreateMediaWatermarkTemplateOutput' => Descriptions::CreateMediaWatermarkTemplateOutput(),
+ 'UpdateMediaWatermarkTemplateOutput' => Descriptions::UpdateMediaWatermarkTemplateOutput(),
+ 'DescribeMediaTemplatesOutput' => Descriptions::DescribeMediaTemplatesOutput(),
+ 'DescribeWorkflowOutput' => Descriptions::DescribeWorkflowOutput(),
+ 'DeleteWorkflowOutput' => Descriptions::DeleteWorkflowOutput(),
+ 'CreateInventoryTriggerJobOutput' => Descriptions::CreateInventoryTriggerJobOutput(),
+ 'DescribeInventoryTriggerJobsOutput' => Descriptions::DescribeInventoryTriggerJobsOutput(),
+ 'DescribeInventoryTriggerJobOutput' => Descriptions::DescribeInventoryTriggerJobOutput(),
+ 'CancelInventoryTriggerJobOutput' => Descriptions::CancelInventoryTriggerJobOutput(),
+ 'CreateMediaNoiseReductionJobsOutput' => Descriptions::CreateMediaNoiseReductionJobsOutput(),
+ 'ImageRepairProcessOutput' => Descriptions::ImageRepairProcessOutput(),
+ 'ImageDetectCarProcessOutput' => Descriptions::ImageDetectCarProcessOutput(),
+ 'ImageAssessQualityProcessOutput' => Descriptions::ImageAssessQualityProcessOutput(),
+ 'ImageSearchOpenOutput' => Descriptions::ImageSearchOpenOutput(),
+ 'ImageSearchAddOutput' => Descriptions::ImageSearchAddOutput(),
+ 'ImageSearchOutput' => Descriptions::ImageSearchOutput(),
+ 'ImageSearchDeleteOutput' => Descriptions::ImageSearchDeleteOutput(),
+ 'BindCiServiceOutput' => Descriptions::BindCiServiceOutput(),
+ 'GetCiServiceOutput' => Descriptions::GetCiServiceOutput(),
+ 'UnBindCiServiceOutput' => Descriptions::UnBindCiServiceOutput(),
+ 'GetHotLinkOutput' => Descriptions::GetHotLinkOutput(),
+ 'AddHotLinkOutput' => Descriptions::AddHotLinkOutput(),
+ 'OpenOriginProtectOutput' => Descriptions::OpenOriginProtectOutput(),
+ 'GetOriginProtectOutput' => Descriptions::GetOriginProtectOutput(),
+ 'CloseOriginProtectOutput' => Descriptions::CloseOriginProtectOutput(),
+ 'ImageDetectFaceOutput' => Descriptions::ImageDetectFaceOutput(),
+ 'ImageFaceEffectOutput' => Descriptions::ImageFaceEffectOutput(),
+ 'IDCardOCROutput' => Descriptions::IDCardOCROutput(),
+ 'IDCardOCRByUploadOutput' => Descriptions::IDCardOCRByUploadOutput(),
+ 'GetLiveCodeOutput' => Descriptions::GetLiveCodeOutput(),
+ 'GetActionSequenceOutput' => Descriptions::GetActionSequenceOutput(),
+ 'DescribeDocProcessBucketsOutput' => Descriptions::DescribeDocProcessBucketsOutput(),
+ 'UpdateDocProcessQueueOutput' => Descriptions::UpdateDocProcessQueueOutput(),
+ 'CreateMediaQualityEstimateJobsOutput' => Descriptions::CreateMediaQualityEstimateJobsOutput(),
+ 'CreateMediaStreamExtractJobsOutput' => Descriptions::CreateMediaStreamExtractJobsOutput(),
+ )
+ );
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/Signature.php b/server/addons/cos/library/Qcloud/Cos/Signature.php
new file mode 100644
index 0000000..28e03d9
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/Signature.php
@@ -0,0 +1,131 @@
+accessKey = $accessKey;
+ $this->secretKey = $secretKey;
+ $this->options = $options;
+ $this->token = $token;
+ $this->signHeader = [
+ 'cache-control',
+ 'content-disposition',
+ 'content-encoding',
+ 'content-length',
+ 'content-md5',
+ 'content-type',
+ 'expect',
+ 'expires',
+ 'host',
+ 'if-match',
+ 'if-modified-since',
+ 'if-none-match',
+ 'if-unmodified-since',
+ 'origin',
+ 'range',
+ 'response-cache-control',
+ 'response-content-disposition',
+ 'response-content-encoding',
+ 'response-content-language',
+ 'response-content-type',
+ 'response-expires',
+ 'transfer-encoding',
+ 'versionid',
+ ];
+ date_default_timezone_set( 'PRC' );
+ }
+
+ public function __destruct() {
+ }
+
+ public function needCheckHeader( $header ) {
+ if ( startWith( $header, 'x-cos-' ) ) {
+ return true;
+ }
+ if ( in_array( $header, $this->signHeader ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ public function signRequest( RequestInterface $request ) {
+ $authorization = $this->createAuthorization( $request );
+ return $request->withHeader( 'Authorization', $authorization );
+ }
+
+ public function createAuthorization( RequestInterface $request, $expires = '+30 minutes' ) {
+ if ( is_null( $expires ) || !strtotime( $expires )) {
+ $expires = '+30 minutes';
+ }
+ $signTime = ( string )( time() - 60 ) . ';' . ( string )( strtotime( $expires ) );
+ $urlParamListArray = [];
+ foreach ( explode( '&', $request->getUri()->getQuery() ) as $query ) {
+ if (!empty($query)) {
+ $tmpquery = explode( '=', $query );
+ //为了保证CI的key中有=号的情况也能正常通过,ci在这层之前已经encode了,这里需要拆开重新encode,防止上方explode拆错
+ $key = strtolower( rawurlencode(urldecode($tmpquery[0])) );
+ if (count($tmpquery) >= 2) {
+ $value = $tmpquery[1];
+ } else {
+ $value = "";
+ }
+ //host开关
+ if (!$this->options['signHost'] && $key == 'host') {
+ continue;
+ }
+ $urlParamListArray[$key] = $key. '='. $value;
+ }
+ }
+ ksort($urlParamListArray);
+ $urlParamList = join(';', array_keys($urlParamListArray));
+ $httpParameters = join('&', array_values($urlParamListArray));
+
+ $headerListArray = [];
+ foreach ( $request->getHeaders() as $key => $value ) {
+ $key = strtolower( urlencode( $key ) );
+ $value = rawurlencode( $value[0] );
+ if ( !$this->options['signHost'] && $key == 'host' ) {
+ continue;
+ }
+ if ( $this->needCheckHeader( $key ) ) {
+ $headerListArray[$key] = $key. '='. $value;
+ }
+ }
+ ksort($headerListArray);
+ $headerList = join(';', array_keys($headerListArray));
+ $httpHeaders = join('&', array_values($headerListArray));
+ $httpString = strtolower( $request->getMethod() ) . "\n" . urldecode( $request->getUri()->getPath() ) . "\n" . $httpParameters.
+ "\n". $httpHeaders. "\n";
+ $sha1edHttpString = sha1( $httpString );
+ $stringToSign = "sha1\n$signTime\n$sha1edHttpString\n";
+ $signKey = hash_hmac( 'sha1', $signTime, trim($this->secretKey) );
+ $signature = hash_hmac( 'sha1', $stringToSign, $signKey );
+ $authorization = 'q-sign-algorithm=sha1&q-ak='. trim($this->accessKey) .
+ "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=$headerList&q-url-param-list=$urlParamList&" .
+ "q-signature=$signature";
+ return $authorization;
+ }
+
+ public function createPresignedUrl( RequestInterface $request, $expires = '+30 minutes' ) {
+ $authorization = $this->createAuthorization( $request, $expires);
+ $uri = $request->getUri();
+ $query = 'sign='.urlencode( $authorization ) . '&' . $uri->getQuery();
+ if ( $this->token != null ) {
+ $query = $query.'&x-cos-security-token='.$this->token;
+ }
+ $uri = $uri->withQuery( $query );
+ return $uri;
+ }
+}
diff --git a/server/addons/cos/library/Qcloud/Cos/SignatureMiddleware.php b/server/addons/cos/library/Qcloud/Cos/SignatureMiddleware.php
new file mode 100644
index 0000000..ff82554
--- /dev/null
+++ b/server/addons/cos/library/Qcloud/Cos/SignatureMiddleware.php
@@ -0,0 +1,23 @@
+nextHandler = $nextHandler;
+ $this->signature = new Signature($accessKey, $secretKey, $options);
+ }
+
+ public function __invoke(RequestInterface $request, array $options) {
+ $fn = $this->nextHandler;
+ return $fn($this->signature->signRequest($request), $options);
+ }
+}
diff --git a/server/addons/epay/.addonrc b/server/addons/epay/.addonrc
new file mode 100644
index 0000000..09a5d1d
--- /dev/null
+++ b/server/addons/epay/.addonrc
@@ -0,0 +1 @@
+{"files":["application\\admin\\controller\\Epay.php","public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\screenshot-alipay.png","public\\assets\\addons\\epay\\images\\screenshot-wechat.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\js\\jquery.qrcode.min.js","public\\assets\\addons\\epay\\less\\common.less"],"license":"regular","licenseto":"26425","licensekey":"AyhmQJMnPsNkVt10 3s13AD9ktdfxofIRgPEtUA==","domains":["yjyf"],"licensecodes":[],"validations":["4dceabe25bd363f318cc03da6a62d391"]}
\ No newline at end of file
diff --git a/server/addons/epay/Epay.php b/server/addons/epay/Epay.php
new file mode 100644
index 0000000..952b5bd
--- /dev/null
+++ b/server/addons/epay/Epay.php
@@ -0,0 +1,112 @@
+actionBegin();
+ }
+
+ // 插件方法加载开始
+ public function addonActionBegin()
+ {
+ $this->actionBegin();
+ }
+
+ // 模块控制器方法加载开始
+ public function actionBegin()
+ {
+ //添加命名空间
+ if (!class_exists('\Yansongda\Pay\Pay')) {
+
+ //SDK版本
+ $version = Service::getSdkVersion();
+
+ $libraryDir = ADDON_PATH . 'epay' . DS . 'library' . DS;
+
+ if (PHP_VERSION_ID >= 80000 && $version == Service::SDK_VERSION_V3) {
+ $yansongdaDir = $libraryDir . $version . 'new' . DS . 'Yansongda' . DS;
+
+ // PHP 8.0+ 版本且为V3接口,需要加载 Artful 的命名空间
+ Loader::addNamespace('Yansongda\Artful', $yansongdaDir . 'Artful' . DS);
+ require_once $yansongdaDir . 'Artful' . DS . 'Functions.php';
+ } else {
+ $yansongdaDir = $libraryDir . $version . DS . 'Yansongda' . DS;
+ }
+
+ // 注册Pay命名空间
+ Loader::addNamespace('Yansongda\Pay', $yansongdaDir . 'Pay' . DS);
+
+ $checkArr = [
+ '\Hyperf\Context\Context' => 'context',
+ '\Hyperf\Contract\Castable' => 'contract',
+ '\Hyperf\Engine\Constant' => 'engine',
+ '\Hyperf\Macroable\Macroable' => 'macroable',
+ '\Hyperf\Pimple\Container' => 'pimple',
+ '\Hyperf\Utils\Arr' => 'utils',
+ ];
+ foreach ($checkArr as $index => $item) {
+ if (!class_exists($index)) {
+ Loader::addNamespace(substr($index, 1, strrpos($index, '\\') - 1), $libraryDir . 'hyperf' . DS . $item . DS . 'src' . DS);
+ }
+ }
+
+ if (!class_exists('\Yansongda\Supports\Logger')) {
+ Loader::addNamespace('Yansongda\Supports', $yansongdaDir . 'Supports' . DS);
+ }
+
+ // V3需载入辅助函数
+ if ($version == Service::SDK_VERSION_V3) {
+ require_once $yansongdaDir . 'Pay' . DS . 'Functions.php';
+ }
+ }
+ }
+}
diff --git a/server/addons/epay/certs/alipayCertPublicKey.crt b/server/addons/epay/certs/alipayCertPublicKey.crt
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/certs/alipayRootCert.crt b/server/addons/epay/certs/alipayRootCert.crt
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/certs/apiclient_cert.pem b/server/addons/epay/certs/apiclient_cert.pem
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/certs/apiclient_key.pem b/server/addons/epay/certs/apiclient_key.pem
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/certs/appCertPublicKey.crt b/server/addons/epay/certs/appCertPublicKey.crt
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/certs/public_key.pem b/server/addons/epay/certs/public_key.pem
new file mode 100644
index 0000000..e69de29
diff --git a/server/addons/epay/config.html b/server/addons/epay/config.html
new file mode 100644
index 0000000..325f89b
--- /dev/null
+++ b/server/addons/epay/config.html
@@ -0,0 +1,529 @@
+
+
diff --git a/server/addons/epay/config.php b/server/addons/epay/config.php
new file mode 100644
index 0000000..92607f2
--- /dev/null
+++ b/server/addons/epay/config.php
@@ -0,0 +1,72 @@
+ 'version',
+ 'title' => 'API版本(请勿修改该值)',
+ 'type' => 'radio',
+ 'content' => [],
+ 'value' => 'v2',
+ 'rule' => '',
+ 'msg' => '',
+ 'tip' => 'V2版本只支持微信支付V2密钥,V3版本只支持微信支付V3密钥,请勿修改该值!!!',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'wechat',
+ 'title' => '微信',
+ 'type' => 'array',
+ 'content' => [],
+ 'value' => [
+ 'appid' => '',
+ 'app_id' => '',
+ 'app_secret' => '',
+ 'miniapp_id' => '',
+ 'mch_id' => '',
+ 'key' => '',
+ 'key_v3' => '',
+ 'public_key_id' => '',
+ 'public_key' => '/addons/epay/certs/public_key.pem',
+ 'cert_client' => '/addons/epay/certs/apiclient_cert.pem',
+ 'cert_key' => '/addons/epay/certs/apiclient_key.pem',
+ 'mode' => 'normal',
+ 'sub_mch_id' => '',
+ 'sub_appid' => '',
+ 'sub_app_id' => '',
+ 'sub_miniapp_id' => '',
+ 'notify_url' => '',
+ 'log' => '1',
+ ],
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '微信参数配置',
+ 'ok' => '',
+ 'extend' => '',
+ ],
+ [
+ 'name' => 'alipay',
+ 'title' => '支付宝',
+ 'type' => 'array',
+ 'content' => [],
+ 'value' => [
+ 'app_id' => '',
+ 'mode' => 'normal',
+ 'notify_url' => '/addons/epay/api/notifyx/type/alipay',
+ 'return_url' => '/addons/epay/api/returnx/type/alipay',
+ 'private_key' => '',
+ 'signtype' => 'cert',
+ 'pid' => '',
+ 'ali_public_key' => '',
+ 'app_cert_public_key' => '',
+ 'alipay_root_cert' => '',
+ 'log' => '1',
+ 'scanpay' => '0',
+ ],
+ 'rule' => 'required',
+ 'msg' => '',
+ 'tip' => '支付宝参数配置',
+ 'ok' => '',
+ 'extend' => '',
+ ]
+];
diff --git a/server/addons/epay/controller/Api.php b/server/addons/epay/controller/Api.php
new file mode 100644
index 0000000..27340e4
--- /dev/null
+++ b/server/addons/epay/controller/Api.php
@@ -0,0 +1,234 @@
+request->filter('trim');
+ $out_trade_no = $this->request->request("out_trade_no");
+ $title = $this->request->request("title");
+ $amount = $this->request->request('amount');
+ $type = $this->request->request('type', $this->request->request('paytype'));
+ $method = $this->request->request('method', 'web');
+ $openid = $this->request->request('openid', '');
+ $auth_code = $this->request->request('auth_code', '');
+ $notifyurl = $this->request->request('notifyurl', '');
+ $returnurl = $this->request->request('returnurl', '');
+
+ if (!$amount || $amount < 0) {
+ $this->error("支付金额必须大于0");
+ }
+
+ if (!$type || !in_array($type, ['alipay', 'wechat'])) {
+ $this->error("支付类型错误");
+ }
+
+ $params = [
+ 'type' => $type,
+ 'out_trade_no' => $out_trade_no,
+ 'title' => $title,
+ 'amount' => $amount,
+ 'method' => $method,
+ 'openid' => $openid,
+ 'auth_code' => $auth_code,
+ 'notifyurl' => $notifyurl,
+ 'returnurl' => $returnurl,
+ ];
+ return Service::submitOrder($params);
+ }
+
+ /**
+ * 微信支付(公众号支付&PC扫码支付)
+ */
+ public function wechat()
+ {
+ $config = Service::getConfig('wechat');
+
+ $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+ $isMobile = $this->request->isMobile();
+ $this->view->assign("isWechat", $isWechat);
+ $this->view->assign("isMobile", $isMobile);
+
+ //发起PC支付(Scan支付)(PC扫码模式)
+ if ($this->request->isAjax()) {
+ $pay = Pay::wechat($config);
+ $orderid = $this->request->post("orderid");
+ try {
+ $result = Service::isVersionV3() ? $pay->find(['out_trade_no' => $orderid]) : $pay->find($orderid, 'scan');
+ $this->success("", "", ['status' => $result['trade_state'] ?? 'NOTPAY']);
+ } catch (GatewayException $e) {
+ $this->error("查询失败(1001)");
+ }
+ }
+
+ $orderData = Session::get("wechatorderdata");
+ if (!$orderData) {
+ $this->error("请求参数错误");
+ }
+ if ($isWechat && $isMobile) {
+ //发起公众号(jsapi支付),openid必须
+
+ //如果没有openid,则自动去获取openid
+ if (!isset($orderData['openid']) || !$orderData['openid']) {
+ $orderData['openid'] = Service::getOpenid();
+ }
+
+ $orderData['method'] = 'mp';
+ $type = 'jsapi';
+ $payData = Service::submitOrder($orderData);
+ if (!isset($payData['paySign'])) {
+ $this->error("创建订单失败,请返回重试", "");
+ }
+ } else {
+ $orderData['method'] = 'scan';
+ $type = 'pc';
+ $payData = Service::submitOrder($orderData);
+ if (!isset($payData['code_url'])) {
+ $this->error("创建订单失败,请返回重试", "");
+ }
+ }
+ $this->view->assign("orderData", $orderData);
+ $this->view->assign("payData", $payData);
+ $this->view->assign("type", $type);
+
+ $this->view->assign("title", "微信支付");
+ return $this->view->fetch();
+ }
+
+ /**
+ * 支付宝支付(PC扫码支付)
+ */
+ public function alipay()
+ {
+ $config = Service::getConfig('alipay');
+
+ $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+ $isMobile = $this->request->isMobile();
+ $this->view->assign("isWechat", $isWechat);
+ $this->view->assign("isMobile", $isMobile);
+
+ if ($this->request->isAjax()) {
+ $orderid = $this->request->post("orderid");
+ $pay = Pay::alipay($config);
+ try {
+ $result = $pay->find(['out_trade_no' => $orderid]);
+ if ($result['code'] == '10000' && $result['trade_status'] == 'TRADE_SUCCESS') {
+ $this->success("", "", ['status' => $result['trade_status']]);
+ } else {
+ $this->error("查询失败");
+ }
+ } catch (GatewayException $e) {
+ $this->error("查询失败(1001)");
+ }
+ }
+
+ //发起PC支付(Scan支付)(PC扫码模式)
+ $orderData = Session::get("alipayorderdata");
+ if (!$orderData) {
+ $this->error("请求参数错误");
+ }
+
+ $orderData['method'] = 'scan';
+ $payData = Service::submitOrder($orderData);
+ if (!isset($payData['qr_code'])) {
+ $this->error("创建订单失败,请返回重试");
+ }
+
+ $type = 'pc';
+ $this->view->assign("orderData", $orderData);
+ $this->view->assign("payData", $payData);
+ $this->view->assign("type", $type);
+ $this->view->assign("title", "支付宝支付");
+ return $this->view->fetch();
+ }
+
+ /**
+ * 支付成功回调
+ */
+ public function notifyx()
+ {
+ $paytype = $this->request->param('paytype');
+ $pay = Service::checkNotify($paytype);
+ if (!$pay) {
+ return json(['code' => 'FAIL', 'message' => '失败'], 500, ['Content-Type' => 'application/json']);
+ }
+
+ // 获取回调数据,V3和V2的回调接收不同
+ $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
+
+ try {
+ //微信支付V3返回和V2不同
+ if (Service::isVersionV3() && $paytype === 'wechat') {
+ $data = $data['resource']['ciphertext'];
+ $data['total_fee'] = $data['amount']['total'];
+ }
+
+ \think\Log::record($data);
+ //获取支付金额、订单号
+ $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
+ $out_trade_no = $data['out_trade_no'];
+
+ \think\Log::record("回调成功,订单号:{$out_trade_no},金额:{$payamount}");
+
+ //你可以在此编写订单逻辑
+ } catch (Exception $e) {
+ \think\Log::record("回调逻辑处理错误:" . $e->getMessage(), "error");
+ }
+
+ //下面这句必须要执行,且在此之前不能有任何输出
+ if (Service::isVersionV3()) {
+ return $pay->success()->getBody()->getContents();
+ } else {
+ return $pay->success()->send();
+ }
+ }
+
+ /**
+ * 支付成功返回
+ */
+ public function returnx()
+ {
+ $paytype = $this->request->param('paytype');
+ if (Service::checkReturn($paytype)) {
+ echo '签名错误';
+ return;
+ }
+
+ //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
+ $this->success("恭喜你!支付成功!", addon_url("epay/index/index"));
+ }
+
+}
diff --git a/server/addons/epay/controller/Index.php b/server/addons/epay/controller/Index.php
new file mode 100644
index 0000000..ea5a25a
--- /dev/null
+++ b/server/addons/epay/controller/Index.php
@@ -0,0 +1,131 @@
+error("仅在开发环境下查看");
+ }
+ }
+
+ public function index()
+ {
+ $this->view->assign("title", "微信支付宝整合");
+ return $this->view->fetch();
+ }
+
+ /**
+ * 体验,仅供开发测试
+ */
+ public function experience()
+ {
+ $amount = $this->request->post('amount');
+ $type = $this->request->post('type');
+ $method = $this->request->post('method');
+ $openid = $this->request->post('openid', "");
+
+ if (!$amount || $amount < 0) {
+ $this->error("支付金额必须大于0");
+ }
+
+ if (!$type || !in_array($type, ['alipay', 'wechat'])) {
+ $this->error("支付类型不能为空");
+ }
+
+ if (in_array($method, ['miniapp', 'mp']) && !$openid) {
+ $this->error("openid不能为空");
+ }
+
+ //订单号
+ $out_trade_no = date("YmdHis") . mt_rand(100000, 999999);
+
+ //订单标题
+ $title = '测试订单';
+
+ //回调链接
+ $notifyurl = $this->request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
+ $returnurl = $this->request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $out_trade_no;
+
+ $response = Service::submitOrder($amount, $out_trade_no, $type, $title, $notifyurl, $returnurl, $method, $openid);
+
+ return $response;
+ }
+
+ /**
+ * 支付成功,仅供开发测试
+ */
+ public function notifyx()
+ {
+ $paytype = $this->request->param('paytype');
+ $pay = Service::checkNotify($paytype);
+ if (!$pay) {
+ return json(['code' => 'FAIL', 'message' => '失败'], 500, ['Content-Type' => 'application/json']);
+ }
+
+ // 获取回调数据,V3和V2的回调接收不同
+ $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
+
+ try {
+ //微信支付V3返回和V2不同
+ if (Service::isVersionV3() && $paytype === 'wechat') {
+ $data = $data['resource']['ciphertext'];
+ $data['total_fee'] = $data['amount']['total'];
+ }
+
+ \think\Log::record($data);
+ //获取支付金额、订单号
+ $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
+ $out_trade_no = $data['out_trade_no'];
+
+ \think\Log::record("回调成功,订单号:{$out_trade_no},金额:{$payamount}");
+
+ //你可以在此编写订单逻辑
+ } catch (Exception $e) {
+ \think\Log::record("回调逻辑处理错误:" . $e->getMessage(), "error");
+ }
+
+ //下面这句必须要执行,且在此之前不能有任何输出
+ if (Service::isVersionV3()) {
+ return $pay->success()->getBody()->getContents();
+ } else {
+ return $pay->success()->send();
+ }
+ }
+
+ /**
+ * 支付返回,仅供开发测试
+ */
+ public function returnx()
+ {
+ $paytype = $this->request->param('paytype');
+ $out_trade_no = $this->request->param('out_trade_no');
+ $pay = Service::checkReturn($paytype);
+ if (!$pay) {
+ $this->error('签名错误', '');
+ }
+
+ //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
+ $this->success("请返回网站查看支付结果", addon_url("epay/index/index"));
+ }
+}
diff --git a/server/addons/epay/info.ini b/server/addons/epay/info.ini
new file mode 100644
index 0000000..c130145
--- /dev/null
+++ b/server/addons/epay/info.ini
@@ -0,0 +1,10 @@
+name = epay
+title = 微信支付宝整合
+intro = 可用于快速整合微信支付、支付宝支付功能
+author = FastAdmin
+website =
+version = 1.3.15
+state = 1
+url = /addons/epay
+license = regular
+licenseto = 26425
diff --git a/server/addons/epay/library/Collection.php b/server/addons/epay/library/Collection.php
new file mode 100644
index 0000000..8b5743e
--- /dev/null
+++ b/server/addons/epay/library/Collection.php
@@ -0,0 +1,18 @@
+message = $message;
+ $this->code = $code;
+ $this->data = $data;
+ }
+
+}
diff --git a/server/addons/epay/library/RedirectResponse.php b/server/addons/epay/library/RedirectResponse.php
new file mode 100644
index 0000000..aa91e46
--- /dev/null
+++ b/server/addons/epay/library/RedirectResponse.php
@@ -0,0 +1,63 @@
+getContent();
+ }
+
+ public function setTargetUrl($url)
+ {
+ if ('' === ($url ?? '')) {
+ throw new \InvalidArgumentException('无法跳转到空页面');
+ }
+
+ $this->targetUrl = $url;
+
+ $this->setContent(
+ sprintf('
+
+
+
+
+
+ 正在跳转支付 %1$s
+
+
+
+
+
+', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
+
+ $this->headers->set('Location', $url);
+
+ return $this;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->getContent();
+ }
+
+ // 使用 PHP 8 兼容的新序列化方式
+ public function __serialize(): array
+ {
+ return ['content' => $this->content];
+ }
+
+ public function __unserialize(array $data): void
+ {
+ $this->content = $data['content'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/Response.php b/server/addons/epay/library/Response.php
new file mode 100644
index 0000000..c6c8613
--- /dev/null
+++ b/server/addons/epay/library/Response.php
@@ -0,0 +1,46 @@
+getContent();
+ }
+
+ public function jsonSerialize(): mixed
+ {
+ return $this->getContent();
+ }
+
+ public function serialize()
+ {
+ return serialize($this->content);
+ }
+
+ public function unserialize($data)
+ {
+ return $this->content = unserialize($data);
+ }
+
+ /**
+ * (PHP 8.1+) Magic method for serialization.
+ *
+ * @return mixed
+ */
+ public function __serialize()
+ {
+ return $this->content;
+ }
+
+ /**
+ * (PHP 8.1+) Magic method for unserialization.
+ *
+ * @param array $data
+ */
+ public function __unserialize(array $data): void
+ {
+ $this->content = $data;
+ }
+}
diff --git a/server/addons/epay/library/Service.php b/server/addons/epay/library/Service.php
new file mode 100644
index 0000000..92eec5e
--- /dev/null
+++ b/server/addons/epay/library/Service.php
@@ -0,0 +1,494 @@
+ $amount,
+ 'orderid' => $orderid,
+ 'type' => $type,
+ 'title' => $title,
+ 'notifyurl' => $notifyurl,
+ 'returnurl' => $returnurl,
+ 'method' => $method,
+ 'openid' => $openid,
+ 'custom' => $custom,
+ ];
+ } else {
+ $params = $amount;
+ }
+ $type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
+ $method = $params['method'] ?? 'web';
+ $orderid = $params['orderid'] ?? date("YmdHis") . mt_rand(100000, 999999);
+ $amount = $params['amount'] ?? 1;
+ $title = $params['title'] ?? "支付";
+ $auth_code = $params['auth_code'] ?? '';
+ $openid = $params['openid'] ?? '';
+
+ //自定义微信支付宝相关配置
+ $custom = $params['custom'] ?? [];
+
+ //未定义则使用默认回调和跳转
+ $notifyurl = !empty($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
+ $returnurl = !empty($params['returnurl']) ? $params['returnurl'] : $request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $orderid;
+
+ $html = '';
+ $config = Service::getConfig($type, array_merge($custom, ['notify_url' => $notifyurl, 'return_url' => $returnurl]));
+
+ //判断是否移动端或微信内浏览器
+ $isMobile = $request->isMobile();
+ $isWechat = strpos($request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
+
+ $result = null;
+ if ($type == 'alipay') {
+ //如果是PC支付,判断当前环境,进行跳转
+ if ($method == 'web') {
+ //如果是微信环境或后台配置PC使用扫码支付
+ if ($isWechat || $addonConfig['alipay']['scanpay']) {
+ Session::set("alipayorderdata", $params);
+ $url = addon_url('epay/api/alipay', [], true, true);
+ return new RedirectResponse($url);
+ } elseif ($isMobile) {
+ $method = 'wap';
+ }
+ }
+
+ //创建支付对象
+ $pay = Pay::alipay($config);
+ $params = [
+ 'out_trade_no' => $orderid,//你的订单号
+ 'total_amount' => $amount,//单位元
+ 'subject' => $title,
+ ];
+
+ switch ($method) {
+ case 'web':
+ //电脑支付
+ $result = $pay->web($params);
+ break;
+ case 'wap':
+ //手机网页支付
+ $result = $pay->wap($params);
+ break;
+ case 'app':
+ //APP支付
+ $result = $pay->app($params);
+ break;
+ case 'scan':
+ //扫码支付
+ $result = $pay->scan($params);
+ break;
+ case 'pos':
+ //刷卡支付必须要有auth_code
+ $params['auth_code'] = $auth_code;
+ $result = $pay->pos($params);
+ break;
+ case 'mini':
+ case 'miniapp':
+ //小程序支付,直接返回字符串
+ //小程序支付必须要有buyer_id或buyer_open_id
+ if (is_numeric($openid) && strlen($openid) === 16) {
+ $params['buyer_id'] = $openid;
+ } else {
+ $params['buyer_open_id'] = $openid;
+ }
+ $result = $pay->mini($params);
+ break;
+ default:
+ }
+ } else {
+ //如果是PC支付,判断当前环境,进行跳转
+ if ($method == 'web') {
+ //如果是移动端,但不是微信环境
+ if ($isMobile && !$isWechat) {
+ $method = 'wap';
+ } else {
+ Session::set("wechatorderdata", $params);
+ $url = addon_url('epay/api/wechat', [], true, true);
+ return new RedirectResponse($url);
+ }
+ }
+
+ //单位分
+ $total_fee = function_exists('bcmul') ? bcmul($amount, 100) : $amount * 100;
+ $total_fee = (int)$total_fee;
+ $ip = $request->ip();
+ //微信服务商模式时需传递sub_openid参数
+ $openidName = $addonConfig['wechat']['mode'] == 'service' ? 'sub_openid' : 'openid';
+
+ //创建支付对象
+ $pay = Pay::wechat($config);
+
+ if (self::isVersionV3()) {
+ //V3支付
+ $params = [
+ 'out_trade_no' => $orderid,
+ 'description' => $title,
+ 'amount' => [
+ 'total' => $total_fee,
+ ]
+ ];
+ switch ($method) {
+ case 'mp':
+ //公众号支付
+ //公众号支付必须有openid
+ $params['payer'] = [$openidName => $openid];
+ $result = $pay->mp($params);
+ break;
+ case 'wap':
+ //手机网页支付,跳转
+ $params['scene_info'] = [
+ 'payer_client_ip' => $ip,
+ 'h5_info' => [
+ 'type' => 'Wap',
+ ]
+ ];
+ $result = $pay->wap($params);
+ break;
+ case 'app':
+ //APP支付,直接返回字符串
+ $result = $pay->app($params);
+ break;
+ case 'scan':
+ //扫码支付,直接返回字符串
+ $result = $pay->scan($params);
+ break;
+ case 'pos':
+ //刷卡支付,直接返回字符串
+ //刷卡支付必须要有auth_code
+ $params['auth_code'] = $auth_code;
+ $result = $pay->pos($params);
+ break;
+ case 'mini':
+ case 'miniapp':
+ //小程序支付,直接返回字符串
+ //小程序支付必须要有openid
+ $params['payer'] = [$openidName => $openid];
+ $result = $pay->mini($params);
+ break;
+ default:
+ }
+ } else {
+ //V2支付
+ $params = [
+ 'out_trade_no' => $orderid,
+ 'body' => $title,
+ 'total_fee' => $total_fee,
+ ];
+ switch ($method) {
+ case 'mp':
+ //公众号支付
+ //公众号支付必须有openid
+ $params[$openidName] = $openid;
+ $result = $pay->mp($params);
+ break;
+ case 'wap':
+ //手机网页支付,跳转
+ $params['spbill_create_ip'] = $ip;
+ $result = $pay->wap($params);
+ break;
+ case 'app':
+ //APP支付,直接返回字符串
+ $result = $pay->app($params);
+ break;
+ case 'scan':
+ //扫码支付,直接返回字符串
+ $result = $pay->scan($params);
+ break;
+ case 'pos':
+ //刷卡支付,直接返回字符串
+ //刷卡支付必须要有auth_code
+ $params['auth_code'] = $auth_code;
+ $result = $pay->pos($params);
+ break;
+ case 'mini':
+ case 'miniapp':
+ //小程序支付,直接返回字符串
+ //小程序支付必须要有openid
+ $params[$openidName] = $openid;
+ $result = $pay->miniapp($params);
+ break;
+ default:
+ }
+ }
+ }
+
+ //使用重写的Response类、RedirectResponse、Collection类
+ if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
+ $result = new RedirectResponse($result->getTargetUrl());
+ } elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
+ $result = new Response($result->getContent());
+ } elseif ($result instanceof \Yansongda\Supports\Collection) {
+ $result = Collection::make($result->all());
+ } elseif ($result instanceof \GuzzleHttp\Psr7\Response) {
+ $result = new Response($result->getBody());
+ }
+
+ return $result;
+ }
+
+ /**
+ * 验证回调是否成功
+ * @param string $type 支付类型
+ * @param array $custom 自定义配置信息
+ * @return bool|\Yansongda\Pay\Gateways\Alipay|\Yansongda\Pay\Gateways\Wechat|\Yansongda\Pay\Provider\Wechat|\Yansongda\Pay\Provider\Alipay
+ */
+ public static function checkNotify($type, $custom = [])
+ {
+ $type = strtolower($type);
+ if (!in_array($type, ['wechat', 'alipay'])) {
+ return false;
+ }
+
+ $version = self::getSdkVersion();
+
+ try {
+ $config = self::getConfig($type, $custom);
+ $pay = $type == 'wechat' ? Pay::wechat($config) : Pay::alipay($config);
+
+ $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
+ if ($type == 'alipay') {
+ if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
+ return $pay;
+ }
+ } else {
+ return $pay;
+ }
+ } catch (Exception $e) {
+ \think\Log::record("回调请求参数解析错误", "error");
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * 验证返回是否成功,请勿用于判断是否支付成功的逻辑验证
+ * 已弃用
+ *
+ * @param string $type 支付类型
+ * @param array $custom 自定义配置信息
+ * @return bool
+ * @deprecated 已弃用,请勿用于逻辑验证
+ */
+ public static function checkReturn($type, $custom = [])
+ {
+ //由于PC及移动端无法获取请求的参数信息,取消return验证,均返回true
+ return true;
+ }
+
+ /**
+ * 处理证书路径
+ * @param array $config 配置
+ * @param string $field 字段
+ * @return void
+ */
+ private static function processAddonsPath(&$config, $field)
+ {
+ if (isset($config[$field]) && substr($config[$field], 0, 8) == '/addons/') {
+ $config[$field] = ROOT_PATH . str_replace('/', DS, substr($config[$field], 1));
+ }
+ }
+
+ /**
+ * 获取配置
+ * @param string $type 支付类型
+ * @param array $custom 自定义配置,用于覆盖插件默认配置
+ * @return array
+ */
+ public static function getConfig($type = 'wechat', $custom = [])
+ {
+ $addonConfig = get_addon_config('epay');
+ $config = $addonConfig[$type] ?? $addonConfig['wechat'];
+
+ // SDK版本
+ $version = self::getSdkVersion();
+
+ // 处理微信证书路径
+ if ($type === 'wechat') {
+ $certFields = ['cert_client', 'cert_key', 'public_key'];
+ foreach ($certFields as $field) {
+ self::processAddonsPath($config, $field);
+ }
+ }
+
+ // 处理支付宝证书路径
+ if ($type === 'alipay') {
+ $config['signtype'] = $config['signtype'] ?? 'publickey';
+ if ($config['signtype'] == 'cert') {
+ $certFields = ['app_cert_public_key', 'alipay_root_cert', 'ali_public_key'];
+ foreach ($certFields as $field) {
+ self::processAddonsPath($config, $field);
+ }
+ } else {
+ // 如果是普通公钥需要将app_cert_public_key和alipay_root_cert设置为空,不然会导致错误
+ $config['app_cert_public_key'] = '';
+ $config['alipay_root_cert'] = '';
+ }
+ }
+
+ // V3支付
+ if (self::isVersionV3()) {
+ if ($type == 'wechat') {
+ $config['mp_app_id'] = $config['app_id'] ?? '';
+ $config['app_id'] = $config['appid'] ?? '';
+ $config['mini_app_id'] = $config['miniapp_id'] ?? '';
+ $config['combine_mch_id'] = $config['combine_mch_id'] ?? '';
+ $config['mch_secret_key'] = $config['key_v3'] ?? '';
+ $config['mch_secret_cert'] = $config['cert_key'];
+ $config['mch_public_cert_path'] = $config['cert_client'];
+
+ // 配置微信支付公钥ID及证书路径
+ $config['wechat_public_cert_path'] = [
+ ($config['public_key_id'] ?? '') => ($config['public_key'] ?? '')
+ ];
+
+ $config['sub_mp_app_id'] = $config['sub_appid'] ?? '';
+ $config['sub_app_id'] = $config['sub_app_id'] ?? '';
+ $config['sub_mini_app_id'] = $config['sub_miniapp_id'] ?? '';
+ $config['sub_mch_id'] = $config['sub_mch_id'] ?? '';
+ } elseif ($type == 'alipay') {
+ $config['app_secret_cert'] = $config['private_key'] ?? '';
+ $config['app_public_cert_path'] = $config['app_cert_public_key'] ?? '';
+ $config['alipay_public_cert_path'] = $config['ali_public_key'] ?? '';
+ $config['alipay_root_cert_path'] = $config['alipay_root_cert'] ?? '';
+ $config['service_provider_id'] = $config['pid'] ?? '';
+ }
+ $modeArr = ['normal' => 0, 'dev' => 1, 'service' => 2];
+ $config['mode'] = $modeArr[$config['mode']] ?? 0;
+ }
+
+ // 日志
+ if ($config['log']) {
+ $config['log'] = [
+ 'enable' => true,
+ 'file' => LOG_PATH . 'epaylogs' . DS . $type . '-' . date("Y-m-d") . '.log',
+ 'level' => 'debug'
+ ];
+ } else {
+ $config['log'] = [
+ 'enable' => false,
+ ];
+ }
+
+ // GuzzleHttp配置,可选
+ $config['http'] = [
+ 'timeout' => 10,
+ 'connect_timeout' => 10,
+ // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
+ ];
+
+ $config['notify_url'] = empty($config['notify_url']) ? addon_url('epay/api/notifyx', [], false) . '/type/' . $type : $config['notify_url'];
+ $config['notify_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? request()->root(true) . $config['notify_url'] : $config['notify_url'];
+ $config['return_url'] = empty($config['return_url']) ? addon_url('epay/api/returnx', [], false) . '/type/' . $type : $config['return_url'];
+ $config['return_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? request()->root(true) . $config['return_url'] : $config['return_url'];
+
+ //合并自定义配置
+ $config = array_merge($config, $custom);
+
+ //v3版本时返回的结构不同
+ if (self::isVersionV3()) {
+ $config = [$type => ['default' => $config], 'logger' => $config['log'], 'http' => $config['http'], '_force' => true];
+
+ }
+ return $config;
+ }
+
+ /**
+ * 获取微信Openid
+ *
+ * @param array $custom 自定义配置信息
+ * @return mixed|string
+ */
+ public static function getOpenid($custom = [])
+ {
+ $openid = '';
+ $auth = Auth::instance();
+ if ($auth->isLogin()) {
+ $third = get_addon_info('third');
+ if ($third && $third['state']) {
+ $thirdInfo = Third::where('user_id', $auth->id)->where('platform', 'wechat')->where('apptype', 'mp')->find();
+ $openid = $thirdInfo ? $thirdInfo['openid'] : '';
+ }
+ }
+ if (!$openid) {
+ $openid = Session::get("openid");
+
+ //如果未传openid,则去读取openid
+ if (!$openid) {
+ $addonConfig = get_addon_config('epay');
+ $wechat = new Wechat($custom['app_id'] ?? $addonConfig['wechat']['app_id'], $custom['app_secret'] ?? $addonConfig['wechat']['app_secret']);
+ $openid = $wechat->getOpenid();
+ }
+ }
+ return $openid;
+ }
+
+ /**
+ * 获取SDK版本
+ * @return mixed|string
+ */
+ public static function getSdkVersion()
+ {
+ $addonConfig = get_addon_config('epay');
+ return $addonConfig['version'] ?? self::SDK_VERSION_V2;
+ }
+
+ /**
+ * 判断是否V2支付
+ * @return bool
+ */
+ public static function isVersionV2()
+ {
+ return self::getSdkVersion() === self::SDK_VERSION_V2;
+ }
+
+ /**
+ * 判断是否V3支付
+ * @return bool
+ */
+ public static function isVersionV3()
+ {
+ return self::getSdkVersion() === self::SDK_VERSION_V3;
+ }
+}
diff --git a/server/addons/epay/library/Wechat.php b/server/addons/epay/library/Wechat.php
new file mode 100644
index 0000000..5877265
--- /dev/null
+++ b/server/addons/epay/library/Wechat.php
@@ -0,0 +1,110 @@
+app_id = $app_id;
+ $this->app_secret = $app_secret;
+ }
+
+ /**
+ * 获取微信授权链接
+ *
+ * @return string
+ */
+ public function getAuthorizeUrl()
+ {
+ $redirect_uri = addon_url('epay/api/wechat', [], true, true);
+ $redirect_uri = urlencode($redirect_uri);
+ $state = \fast\Random::alnum();
+ Session::set('state', $state);
+ return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->app_id}&redirect_uri={$redirect_uri}&response_type=code&scope={$this->scope}&state={$state}#wechat_redirect";
+ }
+
+ /**
+ * 获取微信openid
+ *
+ * @return mixed|string
+ */
+ public function getOpenid()
+ {
+ $openid = Session::get('openid');
+ if (!$openid) {
+ if (!isset($_GET['code'])) {
+ $url = $this->getAuthorizeUrl();
+
+ Header("Location: $url");
+ exit();
+ } else {
+ $state = Session::get('state');
+ if ($state == $_GET['state']) {
+ $code = $_GET['code'];
+ $token = $this->getAccessToken($code);
+ if (!isset($token['openid']) && isset($token['errmsg'])) {
+ exception($token['errmsg']);
+ }
+ $openid = $token['openid'] ?? '';
+ if ($openid) {
+ Session::set("openid", $openid);
+ }
+ }
+ }
+ }
+ return $openid;
+ }
+
+ /**
+ * 获取授权token网页授权
+ *
+ * @param string $code
+ * @return mixed|string
+ */
+ public function getAccessToken($code = '')
+ {
+ $params = [
+ 'appid' => $this->app_id,
+ 'secret' => $this->app_secret,
+ 'code' => $code,
+ 'grant_type' => 'authorization_code'
+ ];
+ $ret = Http::sendRequest('https://api.weixin.qq.com/sns/oauth2/access_token', $params, 'GET');
+ if ($ret['ret']) {
+ $ar = json_decode($ret['msg'], true);
+ return $ar;
+ }
+ return [];
+ }
+
+ public function getJsticket($code = '')
+ {
+ $jsticket = Session::get('jsticket');
+ if (!$jsticket) {
+ $token = $this->getAccessToken($code);
+ $params = [
+ 'access_token' => 'token',
+ 'type' => 'jsapi',
+ ];
+ $ret = Http::sendRequest('https://api.weixin.qq.com/cgi-bin/ticket/getticket', $params, 'GET');
+ if ($ret['ret']) {
+ $ar = json_decode($ret['msg'], true);
+ return $ar;
+ }
+ }
+ return $jsticket;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/context/.gitattributes b/server/addons/epay/library/hyperf/context/.gitattributes
new file mode 100644
index 0000000..27b765f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/context/.gitattributes
@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore
diff --git a/server/addons/epay/library/hyperf/context/LICENSE b/server/addons/epay/library/hyperf/context/LICENSE
new file mode 100644
index 0000000..c35d3f5
--- /dev/null
+++ b/server/addons/epay/library/hyperf/context/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Hyperf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/server/addons/epay/library/hyperf/context/composer.json b/server/addons/epay/library/hyperf/context/composer.json
new file mode 100644
index 0000000..04147af
--- /dev/null
+++ b/server/addons/epay/library/hyperf/context/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "hyperf/context",
+ "description": "A coroutine context library.",
+ "license": "MIT",
+ "keywords": [
+ "php",
+ "swoole",
+ "hyperf",
+ "context"
+ ],
+ "homepage": "https://hyperf.io",
+ "support": {
+ "docs": "https://hyperf.wiki",
+ "issues": "https://github.com/hyperf/hyperf/issues",
+ "pull-request": "https://github.com/hyperf/hyperf/pulls",
+ "source": "https://github.com/hyperf/hyperf"
+ },
+ "require": {
+ "php": ">=7.2",
+ "hyperf/engine": "^1.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "Hyperf\\Context\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HyperfTest\\Context\\": "tests/"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/context/src/Context.php b/server/addons/epay/library/hyperf/context/src/Context.php
new file mode 100644
index 0000000..c8baa4c
--- /dev/null
+++ b/server/addons/epay/library/hyperf/context/src/Context.php
@@ -0,0 +1,112 @@
+ 0) {
+ Coroutine::getContextFor()[$id] = $value;
+ } else {
+ static::$nonCoContext[$id] = $value;
+ }
+ return $value;
+ }
+
+ public static function get(string $id, $default = null, $coroutineId = null)
+ {
+ if (Coroutine::id() > 0) {
+ return Coroutine::getContextFor($coroutineId)[$id] ?? $default;
+ }
+
+ return static::$nonCoContext[$id] ?? $default;
+ }
+
+ public static function has(string $id, $coroutineId = null)
+ {
+ if (Coroutine::id() > 0) {
+ return isset(Coroutine::getContextFor($coroutineId)[$id]);
+ }
+
+ return isset(static::$nonCoContext[$id]);
+ }
+
+ /**
+ * Release the context when you are not in coroutine environment.
+ */
+ public static function destroy(string $id)
+ {
+ unset(static::$nonCoContext[$id]);
+ }
+
+ /**
+ * Copy the context from a coroutine to current coroutine.
+ * This method will delete the origin values in current coroutine.
+ */
+ public static function copy(int $fromCoroutineId, array $keys = []): void
+ {
+ $from = Coroutine::getContextFor($fromCoroutineId);
+ if ($from === null) {
+ return;
+ }
+
+ $current = Coroutine::getContextFor();
+
+ if ($keys) {
+ $map = array_intersect_key($from->getArrayCopy(), array_flip($keys));
+ } else {
+ $map = $from->getArrayCopy();
+ }
+
+ $current->exchangeArray($map);
+ }
+
+ /**
+ * Retrieve the value and override it by closure.
+ */
+ public static function override(string $id, \Closure $closure)
+ {
+ $value = null;
+ if (self::has($id)) {
+ $value = self::get($id);
+ }
+ $value = $closure($value);
+ self::set($id, $value);
+ return $value;
+ }
+
+ /**
+ * Retrieve the value and store it if not exists.
+ * @param mixed $value
+ */
+ public static function getOrSet(string $id, $value)
+ {
+ if (! self::has($id)) {
+ return self::set($id, value($value));
+ }
+ return self::get($id);
+ }
+
+ public static function getContainer()
+ {
+ if (Coroutine::id() > 0) {
+ return Coroutine::getContextFor();
+ }
+
+ return static::$nonCoContext;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/contract/.gitattributes b/server/addons/epay/library/hyperf/contract/.gitattributes
new file mode 100644
index 0000000..27b765f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/contract/.gitattributes
@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore
diff --git a/server/addons/epay/library/hyperf/contract/LICENSE b/server/addons/epay/library/hyperf/contract/LICENSE
new file mode 100644
index 0000000..c35d3f5
--- /dev/null
+++ b/server/addons/epay/library/hyperf/contract/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Hyperf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/server/addons/epay/library/hyperf/contract/composer.json b/server/addons/epay/library/hyperf/contract/composer.json
new file mode 100644
index 0000000..eda94ef
--- /dev/null
+++ b/server/addons/epay/library/hyperf/contract/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "hyperf/contract",
+ "description": "The contracts of Hyperf.",
+ "license": "MIT",
+ "keywords": [
+ "php",
+ "swoole",
+ "hyperf"
+ ],
+ "homepage": "https://hyperf.io",
+ "support": {
+ "docs": "https://hyperf.wiki",
+ "issues": "https://github.com/hyperf/hyperf/issues",
+ "pull-request": "https://github.com/hyperf/hyperf/pulls",
+ "source": "https://github.com/hyperf/hyperf"
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "autoload": {
+ "psr-4": {
+ "Hyperf\\Contract\\": "src/"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/contract/src/ApplicationInterface.php b/server/addons/epay/library/hyperf/contract/src/ApplicationInterface.php
new file mode 100644
index 0000000..24743ed
--- /dev/null
+++ b/server/addons/epay/library/hyperf/contract/src/ApplicationInterface.php
@@ -0,0 +1,16 @@
+setRiskyAllowed(true)
+ ->setRules([
+ '@PSR2' => true,
+ '@Symfony' => true,
+ '@DoctrineAnnotation' => true,
+ '@PhpCsFixer' => true,
+ 'header_comment' => [
+ 'comment_type' => 'PHPDoc',
+ 'header' => $header,
+ 'separate' => 'none',
+ 'location' => 'after_declare_strict',
+ ],
+ 'array_syntax' => [
+ 'syntax' => 'short'
+ ],
+ 'list_syntax' => [
+ 'syntax' => 'short'
+ ],
+ 'concat_space' => [
+ 'spacing' => 'one'
+ ],
+ 'blank_line_before_statement' => [
+ 'statements' => [
+ 'declare',
+ ],
+ ],
+ 'general_phpdoc_annotation_remove' => [
+ 'annotations' => [
+ 'author'
+ ],
+ ],
+ 'ordered_imports' => [
+ 'imports_order' => [
+ 'class', 'function', 'const',
+ ],
+ 'sort_algorithm' => 'alpha',
+ ],
+ 'single_line_comment_style' => [
+ 'comment_types' => [
+ ],
+ ],
+ 'yoda_style' => [
+ 'always_move_variable' => false,
+ 'equal' => false,
+ 'identical' => false,
+ ],
+ 'phpdoc_align' => [
+ 'align' => 'left',
+ ],
+ 'multiline_whitespace_before_semicolons' => [
+ 'strategy' => 'no_multi_line',
+ ],
+ 'constant_case' => [
+ 'case' => 'lower',
+ ],
+ 'class_attributes_separation' => true,
+ 'combine_consecutive_unsets' => true,
+ 'declare_strict_types' => true,
+ 'linebreak_after_opening_tag' => true,
+ 'lowercase_static_reference' => true,
+ 'no_useless_else' => true,
+ 'no_unused_imports' => true,
+ 'not_operator_with_successor_space' => true,
+ 'not_operator_with_space' => false,
+ 'ordered_class_elements' => true,
+ 'php_unit_strict' => false,
+ 'phpdoc_separation' => false,
+ 'single_quote' => true,
+ 'standardize_not_equals' => true,
+ 'multiline_comment_opening_closing' => true,
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('vendor')
+ ->in(__DIR__)
+ )
+ ->setUsingCache(false);
diff --git a/server/addons/epay/library/hyperf/engine/.phpstorm.meta.php b/server/addons/epay/library/hyperf/engine/.phpstorm.meta.php
new file mode 100644
index 0000000..1014069
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/.phpstorm.meta.php
@@ -0,0 +1,6 @@
+" version="1.0" license="MIT" app.name="Hyperf"
+
+ARG timezone
+ARG PHP_VERSION
+
+ENV TIMEZONE=${timezone:-"Asia/Shanghai"}
+ENV COMPOSER_ROOT_VERSION="v1.2.0"
+
+# update
+RUN set -ex \
+ # show php version and extensions
+ && php -v \
+ && php -m \
+ && php --ri swoole \
+ # ---------- some config ----------
+ && cd "/etc/php${PHP_VERSION%\.*}" \
+ # - config PHP
+ && { \
+ echo "upload_max_filesize=128M"; \
+ echo "post_max_size=128M"; \
+ echo "memory_limit=1G"; \
+ echo "date.timezone=${TIMEZONE}"; \
+ } | tee conf.d/99_overrides.ini \
+ # - config timezone
+ && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
+ && echo "${TIMEZONE}" > /etc/timezone \
+ # ---------- clear works ----------
+ && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
+ && echo -e "\033[42;37m Build Completed :).\033[0m\n"
+
+WORKDIR /opt/www
+
+COPY . /opt/www
+
+RUN composer install -o
diff --git a/server/addons/epay/library/hyperf/engine/README.md b/server/addons/epay/library/hyperf/engine/README.md
new file mode 100644
index 0000000..c2888db
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/README.md
@@ -0,0 +1,7 @@
+# Swoole Engine
+
+
+
+```
+composer require hyperf/engine
+```
diff --git a/server/addons/epay/library/hyperf/engine/composer.json b/server/addons/epay/library/hyperf/engine/composer.json
new file mode 100644
index 0000000..04b49ee
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/composer.json
@@ -0,0 +1,49 @@
+{
+ "name": "hyperf/engine",
+ "type": "library",
+ "license": "MIT",
+ "keywords": [
+ "php",
+ "hyperf"
+ ],
+ "description": "",
+ "autoload": {
+ "psr-4": {
+ "Hyperf\\Engine\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HyperfTest\\": "tests"
+ }
+ },
+ "require": {
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "hyperf/guzzle": "^2.2",
+ "phpstan/phpstan": "^1.0",
+ "phpunit/phpunit": "^9.4",
+ "swoole/ide-helper": "dev-master"
+ },
+ "suggest": {
+ "ext-swoole": ">=4.5"
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "optimize-autoloader": true,
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "scripts": {
+ "test": "phpunit -c phpunit.xml --colors=always",
+ "analyse": "phpstan analyse --memory-limit 1024M -l 0 ./src",
+ "cs-fix": "php-cs-fixer fix $1"
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/phpunit.xml b/server/addons/epay/library/hyperf/engine/phpunit.xml
new file mode 100644
index 0000000..d2c615a
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/phpunit.xml
@@ -0,0 +1,15 @@
+
+
+
+ ./tests/
+
+
\ No newline at end of file
diff --git a/server/addons/epay/library/hyperf/engine/src/Channel.php b/server/addons/epay/library/hyperf/engine/src/Channel.php
new file mode 100644
index 0000000..9683a45
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Channel.php
@@ -0,0 +1,142 @@
+ 80000 && SWOOLE_VERSION_ID >= 50000) {
+ class Channel extends \Swoole\Coroutine\Channel implements ChannelInterface
+ {
+ protected bool $closed = false;
+
+ public function push(mixed $data, float $timeout = -1): bool
+ {
+ return parent::push($data, $timeout);
+ }
+
+ public function pop(float $timeout = -1): mixed
+ {
+ return parent::pop($timeout);
+ }
+
+ public function getCapacity(): int
+ {
+ return $this->capacity;
+ }
+
+ public function getLength(): int
+ {
+ return $this->length();
+ }
+
+ public function isAvailable(): bool
+ {
+ return ! $this->isClosing();
+ }
+
+ public function close(): bool
+ {
+ $this->closed = true;
+ return parent::close();
+ }
+
+ public function hasProducers(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function hasConsumers(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isReadable(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isWritable(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isClosing(): bool
+ {
+ return $this->closed || $this->errCode === SWOOLE_CHANNEL_CLOSED;
+ }
+
+ public function isTimeout(): bool
+ {
+ return ! $this->closed && $this->errCode === SWOOLE_CHANNEL_TIMEOUT;
+ }
+ }
+} else {
+ class Channel extends \Swoole\Coroutine\Channel implements ChannelInterface
+ {
+ /**
+ * @var bool
+ */
+ protected $closed = false;
+
+ public function getCapacity(): int
+ {
+ return $this->capacity;
+ }
+
+ public function getLength(): int
+ {
+ return $this->length();
+ }
+
+ public function isAvailable(): bool
+ {
+ return ! $this->isClosing();
+ }
+
+ public function close(): bool
+ {
+ $this->closed = true;
+ return parent::close();
+ }
+
+ public function hasProducers(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function hasConsumers(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isReadable(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isWritable(): bool
+ {
+ throw new RuntimeException('Not supported.');
+ }
+
+ public function isClosing(): bool
+ {
+ return $this->closed || $this->errCode === SWOOLE_CHANNEL_CLOSED;
+ }
+
+ public function isTimeout(): bool
+ {
+ return ! $this->closed && $this->errCode === SWOOLE_CHANNEL_TIMEOUT;
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Constant.php b/server/addons/epay/library/hyperf/engine/src/Constant.php
new file mode 100644
index 0000000..48a347b
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Constant.php
@@ -0,0 +1,25 @@
+ 80000 && SWOOLE_VERSION_ID >= 50000) {
+ interface ChannelInterface
+ {
+ /**
+ * @param float|int $timeout [optional] = -1
+ */
+ public function push(mixed $data, float $timeout = -1): bool;
+
+ /**
+ * @param float $timeout seconds [optional] = -1
+ * @return mixed when pop failed, return false
+ */
+ public function pop(float $timeout = -1): mixed;
+
+ /**
+ * Swow: When the channel is closed, all the data in it will be destroyed.
+ * Swoole: When the channel is closed, the data in it can still be popped out, but push behavior will no longer succeed.
+ */
+ public function close(): bool;
+
+ public function getCapacity(): int;
+
+ public function getLength(): int;
+
+ public function isAvailable(): bool;
+
+ public function hasProducers(): bool;
+
+ public function hasConsumers(): bool;
+
+ public function isEmpty(): bool;
+
+ public function isFull(): bool;
+
+ public function isReadable(): bool;
+
+ public function isWritable(): bool;
+
+ public function isClosing(): bool;
+
+ public function isTimeout(): bool;
+ }
+} else {
+ interface ChannelInterface
+ {
+ /**
+ * @param mixed $data [required]
+ * @param float|int $timeout [optional] = -1
+ * @return bool
+ */
+ public function push($data, $timeout = -1);
+
+ /**
+ * @param float $timeout seconds [optional] = -1
+ * @return mixed when pop failed, return false
+ */
+ public function pop($timeout = -1);
+
+ /**
+ * Swow: When the channel is closed, all the data in it will be destroyed.
+ * Swoole: When the channel is closed, the data in it can still be popped out, but push behavior will no longer succeed.
+ * @return mixed
+ */
+ public function close(): bool;
+
+ /**
+ * @return int
+ */
+ public function getCapacity();
+
+ /**
+ * @return int
+ */
+ public function getLength();
+
+ /**
+ * @return bool
+ */
+ public function isAvailable();
+
+ /**
+ * @return bool
+ */
+ public function hasProducers();
+
+ /**
+ * @return bool
+ */
+ public function hasConsumers();
+
+ /**
+ * @return bool
+ */
+ public function isEmpty();
+
+ /**
+ * @return bool
+ */
+ public function isFull();
+
+ /**
+ * @return bool
+ */
+ public function isReadable();
+
+ /**
+ * @return bool
+ */
+ public function isWritable();
+
+ /**
+ * @return bool
+ */
+ public function isClosing();
+
+ /**
+ * @return bool
+ */
+ public function isTimeout();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Contract/CoroutineInterface.php b/server/addons/epay/library/hyperf/engine/src/Contract/CoroutineInterface.php
new file mode 100644
index 0000000..439e80e
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Contract/CoroutineInterface.php
@@ -0,0 +1,70 @@
+callable = $callable;
+ }
+
+ public static function create(callable $callable, ...$data)
+ {
+ $coroutine = new static($callable);
+ $coroutine->execute(...$data);
+ return $coroutine;
+ }
+
+ public function execute(...$data)
+ {
+ $this->id = SwooleCo::create($this->callable, ...$data);
+ return $this;
+ }
+
+ public function getId()
+ {
+ if (is_null($this->id)) {
+ throw new RuntimeException('Coroutine was not be executed.');
+ }
+ return $this->id;
+ }
+
+ public static function id()
+ {
+ return SwooleCo::getCid();
+ }
+
+ public static function pid(?int $id = null)
+ {
+ if ($id) {
+ $cid = SwooleCo::getPcid($id);
+ if ($cid === false) {
+ throw new CoroutineDestroyedException(sprintf('Coroutine #%d has been destroyed.', $id));
+ }
+ } else {
+ $cid = SwooleCo::getPcid();
+ }
+ if ($cid === false) {
+ throw new RunningInNonCoroutineException('Non-Coroutine environment don\'t has parent coroutine id.');
+ }
+ return max(0, $cid);
+ }
+
+ public static function set(array $config)
+ {
+ SwooleCo::set($config);
+ }
+
+ /**
+ * @return null|\ArrayObject
+ */
+ public static function getContextFor(?int $id = null)
+ {
+ if ($id === null) {
+ return SwooleCo::getContext();
+ }
+
+ return SwooleCo::getContext($id);
+ }
+
+ public static function defer(callable $callable)
+ {
+ SwooleCo::defer($callable);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Exception/CoroutineDestroyedException.php b/server/addons/epay/library/hyperf/engine/src/Exception/CoroutineDestroyedException.php
new file mode 100644
index 0000000..1218249
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Exception/CoroutineDestroyedException.php
@@ -0,0 +1,16 @@
+setMethod($method);
+ $this->setData($contents);
+ $this->setHeaders($this->encodeHeaders($headers));
+ $this->execute($path);
+ if ($this->errCode !== 0) {
+ throw new HttpClientException($this->errMsg, $this->errCode);
+ }
+ return new RawResponse(
+ $this->statusCode,
+ $this->decodeHeaders($this->headers ?? []),
+ $this->body,
+ $version
+ );
+ }
+
+ /**
+ * @param string[] $headers
+ * @return string[][]
+ */
+ private function decodeHeaders(array $headers): array
+ {
+ $result = [];
+ foreach ($headers as $name => $header) {
+ // The key of header is lower case.
+ $result[$name][] = $header;
+ }
+ if ($this->set_cookie_headers) {
+ $result['set-cookie'] = $this->set_cookie_headers;
+ }
+ return $result;
+ }
+
+ /**
+ * Swoole engine not support two dimensional array.
+ * @param string[][] $headers
+ * @return string[]
+ */
+ private function encodeHeaders(array $headers): array
+ {
+ $result = [];
+ foreach ($headers as $name => $value) {
+ $result[$name] = is_array($value) ? implode(',', $value) : $value;
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Http/FdGetter.php b/server/addons/epay/library/hyperf/engine/src/Http/FdGetter.php
new file mode 100644
index 0000000..51ca079
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Http/FdGetter.php
@@ -0,0 +1,22 @@
+fd;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Http/RawResponse.php b/server/addons/epay/library/hyperf/engine/src/Http/RawResponse.php
new file mode 100644
index 0000000..8a8fb20
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Http/RawResponse.php
@@ -0,0 +1,47 @@
+statusCode = $statusCode;
+ $this->headers = $headers;
+ $this->body = $body;
+ $this->version = $version;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/engine/src/Socket.php b/server/addons/epay/library/hyperf/engine/src/Socket.php
new file mode 100644
index 0000000..17a8a1b
--- /dev/null
+++ b/server/addons/epay/library/hyperf/engine/src/Socket.php
@@ -0,0 +1,16 @@
+
+ */
+ protected $events = [];
+
+ public function __construct(Response $connection, Request $request)
+ {
+ $this->connection = $connection;
+ $this->connection->upgrade();
+ }
+
+ public function on(string $event, callable $callback): void
+ {
+ $this->events[$event] = $callback;
+ }
+
+ public function start(): void
+ {
+ while (true) {
+ $frame = $this->connection->recv();
+ if ($frame === false || $frame instanceof CloseFrame || $frame === '') {
+ $callback = $this->events[static::ON_CLOSE];
+ $callback($this->connection, $this->connection->fd);
+ break;
+ }
+
+ $callback = $this->events[static::ON_MESSAGE];
+ $callback($this->connection, $frame);
+ }
+
+ $this->connection = null;
+ $this->events = [];
+ }
+}
diff --git a/server/addons/epay/library/hyperf/macroable/.gitattributes b/server/addons/epay/library/hyperf/macroable/.gitattributes
new file mode 100644
index 0000000..27b765f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/macroable/.gitattributes
@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore
diff --git a/server/addons/epay/library/hyperf/macroable/LICENSE b/server/addons/epay/library/hyperf/macroable/LICENSE
new file mode 100644
index 0000000..10b0738
--- /dev/null
+++ b/server/addons/epay/library/hyperf/macroable/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+Copyright (c) Hyperf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/server/addons/epay/library/hyperf/macroable/composer.json b/server/addons/epay/library/hyperf/macroable/composer.json
new file mode 100644
index 0000000..c828d10
--- /dev/null
+++ b/server/addons/epay/library/hyperf/macroable/composer.json
@@ -0,0 +1,39 @@
+{
+ "name": "hyperf/macroable",
+ "description": "Hyperf Macroable package which come from illuminate/macroable",
+ "license": "MIT",
+ "keywords": [
+ "php",
+ "swoole",
+ "hyperf",
+ "macroable"
+ ],
+ "homepage": "https://hyperf.io",
+ "support": {
+ "docs": "https://hyperf.wiki",
+ "issues": "https://github.com/hyperf/hyperf/issues",
+ "pull-request": "https://github.com/hyperf/hyperf/pulls",
+ "source": "https://github.com/hyperf/hyperf"
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Hyperf\\Macroable\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HyperfTest\\Macroable\\": "tests/"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/macroable/src/Macroable.php b/server/addons/epay/library/hyperf/macroable/src/Macroable.php
new file mode 100644
index 0000000..b58417a
--- /dev/null
+++ b/server/addons/epay/library/hyperf/macroable/src/Macroable.php
@@ -0,0 +1,129 @@
+bindTo(null, static::class);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @throws \BadMethodCallException
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (! static::hasMacro($method)) {
+ throw new BadMethodCallException(sprintf(
+ 'Method %s::%s does not exist.',
+ static::class,
+ $method
+ ));
+ }
+
+ $macro = static::$macros[$method];
+
+ if ($macro instanceof Closure) {
+ $macro = $macro->bindTo($this, static::class);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @param string $name
+ * @param callable|object $macro
+ */
+ public static function macro($name, $macro)
+ {
+ static::$macros[$name] = $macro;
+ }
+
+ /**
+ * Mix another object into the class.
+ *
+ * @param object $mixin
+ * @param bool $replace
+ *
+ * @throws \ReflectionException
+ */
+ public static function mixin($mixin, $replace = true)
+ {
+ $methods = (new ReflectionClass($mixin))->getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ if ($replace || ! static::hasMacro($method->name)) {
+ $method->setAccessible(true);
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+ }
+
+ /**
+ * Checks if macro is registered.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/pimple/.gitattributes b/server/addons/epay/library/hyperf/pimple/.gitattributes
new file mode 100644
index 0000000..27b765f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/.gitattributes
@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore
diff --git a/server/addons/epay/library/hyperf/pimple/.gitignore b/server/addons/epay/library/hyperf/pimple/.gitignore
new file mode 100644
index 0000000..7e11e88
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/.gitignore
@@ -0,0 +1,4 @@
+/vendor/
+composer.lock
+*.cache
+*.log
\ No newline at end of file
diff --git a/server/addons/epay/library/hyperf/pimple/.php-cs-fixer.php b/server/addons/epay/library/hyperf/pimple/.php-cs-fixer.php
new file mode 100644
index 0000000..4e06aab
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/.php-cs-fixer.php
@@ -0,0 +1,92 @@
+setRiskyAllowed(true)
+ ->setRules([
+ '@PSR2' => true,
+ '@Symfony' => true,
+ '@DoctrineAnnotation' => true,
+ '@PhpCsFixer' => true,
+ 'header_comment' => [
+ 'comment_type' => 'PHPDoc',
+ 'header' => $header,
+ 'separate' => 'none',
+ 'location' => 'after_declare_strict',
+ ],
+ 'array_syntax' => [
+ 'syntax' => 'short'
+ ],
+ 'list_syntax' => [
+ 'syntax' => 'short'
+ ],
+ 'concat_space' => [
+ 'spacing' => 'one'
+ ],
+ 'blank_line_before_statement' => [
+ 'statements' => [
+ 'declare',
+ ],
+ ],
+ 'general_phpdoc_annotation_remove' => [
+ 'annotations' => [
+ 'author'
+ ],
+ ],
+ 'ordered_imports' => [
+ 'imports_order' => [
+ 'class', 'function', 'const',
+ ],
+ 'sort_algorithm' => 'alpha',
+ ],
+ 'single_line_comment_style' => [
+ 'comment_types' => [
+ ],
+ ],
+ 'yoda_style' => [
+ 'always_move_variable' => false,
+ 'equal' => false,
+ 'identical' => false,
+ ],
+ 'phpdoc_align' => [
+ 'align' => 'left',
+ ],
+ 'multiline_whitespace_before_semicolons' => [
+ 'strategy' => 'no_multi_line',
+ ],
+ 'constant_case' => [
+ 'case' => 'lower',
+ ],
+ 'class_attributes_separation' => true,
+ 'combine_consecutive_unsets' => true,
+ 'declare_strict_types' => true,
+ 'linebreak_after_opening_tag' => true,
+ 'lowercase_static_reference' => true,
+ 'no_useless_else' => true,
+ 'no_unused_imports' => true,
+ 'not_operator_with_successor_space' => true,
+ 'not_operator_with_space' => false,
+ 'ordered_class_elements' => true,
+ 'php_unit_strict' => false,
+ 'phpdoc_separation' => false,
+ 'single_quote' => true,
+ 'standardize_not_equals' => true,
+ 'multiline_comment_opening_closing' => true,
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('bin')
+ ->exclude('public')
+ ->exclude('runtime')
+ ->exclude('vendor')
+ ->in(__DIR__)
+ )
+ ->setUsingCache(false);
diff --git a/server/addons/epay/library/hyperf/pimple/.phpstorm.meta.php b/server/addons/epay/library/hyperf/pimple/.phpstorm.meta.php
new file mode 100644
index 0000000..1014069
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/.phpstorm.meta.php
@@ -0,0 +1,6 @@
+set(TranslatorLoaderInterface::class, function () use ($container) {
+ $config = $container->get(ConfigInterface::class);
+ $files = $container->get(Filesystem::class);
+ $path = $config->get('translation.path');
+
+ return make(FileLoader::class, compact('files', 'path'));
+ });
+ }
+}
+```
+
+```php
+set(TranslatorInterface::class, function () use ($container) {
+ $config = $container->get(ConfigInterface::class);
+ $locale = $config->get('translation.locale');
+ $fallbackLocale = $config->get('translation.fallback_locale');
+
+ $loader = $container->get(TranslatorLoaderInterface::class);
+
+ $translator = make(Translator::class, compact('loader', 'locale'));
+ $translator->setFallback((string) $fallbackLocale);
+
+ return $translator;
+ });
+ }
+}
+
+```
+
+3. `EasySwoole` 事件注册器在 `EasySwooleEvent.php` 中,所以我们需要在 `initialize()` 中初始化我们的容器和国际化组件。
+
+> 以下 Config 组件,可以自行封装,这里方便起见直接配置。
+
+```php
+set(ConfigInterface::class, new Config([
+ 'translation' => [
+ 'locale' => 'zh_CN',
+ 'fallback_locale' => 'en',
+ 'path' => EASYSWOOLE_ROOT . '/storage/languages',
+ ],
+ ]));
+ }
+}
+```
+
+4. 修改控制器,使用国际化组件
+
+```php
+get(TranslatorInterface::class);
+
+ $data = [
+ 'message' => $translator->trans('message.hello', ['name' => 'Hyperf']),
+ ];
+
+ $this->response()->write(Json::encode($data));
+ }
+}
+
+```
+
+5. 添加国际化配置
+
+```php
+// storage/languages/en/message.php
+return [
+ 'hello' => 'Hello :name',
+];
+
+// storage/languages/zh_CN/message.php
+return [
+ 'hello' => '你好 :name',
+];
+```
+
+6. 测试
+
+```
+$ curl http://127.0.0.1:9501/
+{"message":"你好 Hyperf"}
+```
+
+
+
diff --git a/server/addons/epay/library/hyperf/pimple/composer.json b/server/addons/epay/library/hyperf/pimple/composer.json
new file mode 100644
index 0000000..b878c0a
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/composer.json
@@ -0,0 +1,53 @@
+{
+ "name": "hyperf/pimple",
+ "type": "library",
+ "license": "MIT",
+ "keywords": [
+ "php",
+ "hyperf",
+ "container",
+ "psr11"
+ ],
+ "description": "Pimple Container",
+ "autoload": {
+ "psr-4": {
+ "Hyperf\\Pimple\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HyperfTest\\": "tests"
+ }
+ },
+ "require": {
+ "php": ">=7.4",
+ "doctrine/instantiator": "^1.0",
+ "hyperf/utils": "^2.2|^3.0",
+ "pimple/pimple": "^3.3"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "mockery/mockery": "^1.3",
+ "phpstan/phpstan": "^1.0",
+ "phpunit/phpunit": ">=7.0"
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "optimize-autoloader": true,
+ "sort-packages": true
+ },
+ "scripts": {
+ "test": "phpunit -c phpunit.xml --colors=always",
+ "analyse": "phpstan analyse --memory-limit 300M -l 0 ./src",
+ "cs-fix": "php-cs-fixer fix $1"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ },
+ "hyperf": {
+ "config": "Hyperf\\Pimple\\ConfigProvider"
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/pimple/phpunit.xml b/server/addons/epay/library/hyperf/pimple/phpunit.xml
new file mode 100644
index 0000000..d2c615a
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/phpunit.xml
@@ -0,0 +1,15 @@
+
+
+
+ ./tests/
+
+
\ No newline at end of file
diff --git a/server/addons/epay/library/hyperf/pimple/src/ConfigProvider.php b/server/addons/epay/library/hyperf/pimple/src/ConfigProvider.php
new file mode 100644
index 0000000..be00f3f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/src/ConfigProvider.php
@@ -0,0 +1,32 @@
+ [
+ ],
+ 'commands' => [
+ ],
+ 'annotations' => [
+ 'scan' => [
+ 'paths' => [
+ __DIR__,
+ ],
+ ],
+ ],
+ ];
+ }
+}
diff --git a/server/addons/epay/library/hyperf/pimple/src/Container.php b/server/addons/epay/library/hyperf/pimple/src/Container.php
new file mode 100644
index 0000000..aab71dc
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/src/Container.php
@@ -0,0 +1,139 @@
+pimple = $pimple;
+ $this->pimple[ContainerInterface::class] = $this;
+ $this->pimple[PsrContainerInterface::class] = $this;
+ }
+
+ public function get($id)
+ {
+ if ($this->has($id)) {
+ return $this->pimple[$id];
+ }
+
+ return $this->pimple[$id] = $this->make($id);
+ }
+
+ public function has($id): bool
+ {
+ return isset($this->pimple[$id]);
+ }
+
+ public function make(string $name, array $parameters = [])
+ {
+ if (! class_exists($name)) {
+ throw new NotFoundException("Entry {$name} is not found.");
+ }
+
+ $ref = $this->reflection[$name] ?? new ReflectionClass($name);
+ $constructor = $ref->getConstructor();
+ $args = [];
+ if ($constructor && $constructor->isPublic()) {
+ $args = $this->resolveParameters($constructor, $parameters);
+ }
+
+ $instance = new $name(...$args);
+
+ $this->reflection[$name] = $ref;
+
+ return $instance;
+ }
+
+ public function set(string $name, $entry): void
+ {
+ $this->pimple[$name] = $entry;
+ }
+
+ public function define(string $name, $definition): void
+ {
+ throw new NotSupportException('Method define is not support.');
+ }
+
+ public function unbind(string $name): void
+ {
+ $this->pimple[$name] = null;
+ }
+
+ protected function resolveParameters(ReflectionMethod $method, $parameters = [])
+ {
+ $args = [];
+ foreach ($method->getParameters() as $index => $parameter) {
+ if (array_key_exists($parameter->getName(), $parameters)) {
+ $value = $parameters[$parameter->getName()];
+ } elseif (array_key_exists($index, $parameters)) {
+ $value = $parameters[$index];
+ } elseif ($parameter->getType() && $this->has($parameter->getType()->getName())) {
+ $value = $this->get($parameter->getType()->getName());
+ } else {
+ if ($parameter->isDefaultValueAvailable() || $parameter->isOptional()) {
+ $value = $this->getParameterDefaultValue($parameter, $method);
+ } else {
+ throw new InvalidDefinitionException(sprintf(
+ 'Parameter $%s of %s has no value defined or guessable',
+ $parameter->getName(),
+ $this->getFunctionName($method)
+ ));
+ }
+ }
+
+ $args[] = $value;
+ }
+
+ return $args;
+ }
+
+ protected function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function)
+ {
+ try {
+ return $parameter->getDefaultValue();
+ } catch (ReflectionException $e) {
+ throw new InvalidDefinitionException(sprintf(
+ 'The parameter "%s" of %s has no type defined or guessable. It has a default value, '
+ . 'but the default value can\'t be read through Reflection because it is a PHP internal class.',
+ $parameter->getName(),
+ $this->getFunctionName($function)
+ ));
+ }
+ }
+
+ private function getFunctionName(ReflectionMethod $method): string
+ {
+ return $method->getName() . '()';
+ }
+}
diff --git a/server/addons/epay/library/hyperf/pimple/src/ContainerFactory.php b/server/addons/epay/library/hyperf/pimple/src/ContainerFactory.php
new file mode 100644
index 0000000..fed4e3d
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/src/ContainerFactory.php
@@ -0,0 +1,37 @@
+providers = $providers;
+ }
+
+ public function __invoke()
+ {
+ $container = new Container(new Pimple\Container());
+ foreach ($this->providers as $provider) {
+ /** @var ProviderInterface $instance */
+ $instance = new $provider();
+ $instance->register($container);
+ }
+
+ return ApplicationContext::setContainer($container);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/pimple/src/Exception/InvalidDefinitionException.php b/server/addons/epay/library/hyperf/pimple/src/Exception/InvalidDefinitionException.php
new file mode 100644
index 0000000..8b9032b
--- /dev/null
+++ b/server/addons/epay/library/hyperf/pimple/src/Exception/InvalidDefinitionException.php
@@ -0,0 +1,18 @@
+=7.2",
+ "doctrine/inflector": "^2.0",
+ "hyperf/context": "~2.2.0",
+ "hyperf/contract": "~2.2.0",
+ "hyperf/engine": "^1.1",
+ "hyperf/macroable": "~2.2.0"
+ },
+ "suggest": {
+ "ext-swoole": "Required to use methods related to swoole (>=4.5).",
+ "symfony/var-dumper": "Required to use the dd function (^5.0).",
+ "symfony/serializer": "Required to use SymfonyNormalizer (^5.0)",
+ "symfony/property-access": "Required to use SymfonyNormalizer (^5.0)",
+ "hyperf/di": "Required to use ExceptionNormalizer",
+ "nikic/php-parser": "Required to use PhpParser. (^4.0)"
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Hyperf\\Utils\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HyperfTest\\Utils\\": "tests/"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "hyperf": {
+ "config": "Hyperf\\Utils\\ConfigProvider"
+ },
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/ApplicationContext.php b/server/addons/epay/library/hyperf/utils/src/ApplicationContext.php
new file mode 100644
index 0000000..dc16b7b
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/ApplicationContext.php
@@ -0,0 +1,41 @@
+all();
+ } elseif (! is_array($values)) {
+ continue;
+ }
+ $results[] = $values;
+ }
+ return array_merge([], ...$results);
+ }
+
+ /**
+ * Cross join the given arrays, returning all possible permutations.
+ *
+ * @param array ...$arrays
+ */
+ public static function crossJoin(...$arrays): array
+ {
+ $results = [[]];
+ foreach ($arrays as $index => $array) {
+ $append = [];
+ foreach ($results as $product) {
+ foreach ($array as $item) {
+ $product[$index] = $item;
+ $append[] = $product;
+ }
+ }
+ $results = $append;
+ }
+ return $results;
+ }
+
+ /**
+ * Divide an array into two arrays. One with keys and the other with values.
+ *
+ * @param array $array
+ * @return array
+ */
+ public static function divide($array)
+ {
+ return [array_keys($array), array_values($array)];
+ }
+
+ /**
+ * Flatten a multi-dimensional associative array with dots.
+ */
+ public static function dot(array $array, string $prepend = ''): array
+ {
+ $results = [];
+ foreach ($array as $key => $value) {
+ if (is_array($value) && ! empty($value)) {
+ $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
+ } else {
+ $results[$prepend . $key] = $value;
+ }
+ }
+ return $results;
+ }
+
+ /**
+ * Get all of the given array except for a specified array of keys.
+ *
+ * @param array|string $keys
+ */
+ public static function except(array $array, $keys): array
+ {
+ static::forget($array, $keys);
+ return $array;
+ }
+
+ /**
+ * Determine if the given key exists in the provided array.
+ *
+ * @param array|\ArrayAccess $array
+ * @param int|string $key
+ */
+ public static function exists($array, $key): bool
+ {
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Return the first element in an array passing a given truth test.
+ *
+ * @param null|mixed $default
+ */
+ public static function first(array $array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ if (empty($array)) {
+ return value($default);
+ }
+ foreach ($array as $item) {
+ return $item;
+ }
+ }
+ foreach ($array as $key => $value) {
+ if (call_user_func($callback, $value, $key)) {
+ return $value;
+ }
+ }
+ return value($default);
+ }
+
+ /**
+ * Return the last element in an array passing a given truth test.
+ *
+ * @param null|mixed $default
+ */
+ public static function last(array $array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ return empty($array) ? value($default) : end($array);
+ }
+ return static::first(array_reverse($array, true), $callback, $default);
+ }
+
+ /**
+ * Flatten a multi-dimensional array into a single level.
+ * @param float|int $depth
+ */
+ public static function flatten(array $array, $depth = INF): array
+ {
+ $result = [];
+ foreach ($array as $item) {
+ $item = $item instanceof Collection ? $item->all() : $item;
+ if (! is_array($item)) {
+ $result[] = $item;
+ } elseif ($depth === 1) {
+ $result = array_merge($result, array_values($item));
+ } else {
+ $result = array_merge($result, static::flatten($item, $depth - 1));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Remove one or many array items from a given array using "dot" notation.
+ *
+ * @param array|string $keys
+ */
+ public static function forget(array &$array, $keys): void
+ {
+ $original = &$array;
+ $keys = (array) $keys;
+ if (count($keys) === 0) {
+ return;
+ }
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+ continue;
+ }
+ $parts = explode('.', (string) $key);
+ // clean up before each pass
+ $array = &$original;
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+ if (isset($array[$part]) && is_array($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ /**
+ * Get an item from an array using "dot" notation.
+ *
+ * @param array|\ArrayAccess $array
+ * @param null|int|string $key
+ * @param mixed $default
+ */
+ public static function get($array, $key = null, $default = null)
+ {
+ if (! static::accessible($array)) {
+ return value($default);
+ }
+ if (is_null($key)) {
+ return $array;
+ }
+ if (static::exists($array, $key)) {
+ return $array[$key];
+ }
+ if (! is_string($key) || strpos($key, '.') === false) {
+ return $array[$key] ?? value($default);
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($array) && static::exists($array, $segment)) {
+ $array = $array[$segment];
+ } else {
+ return value($default);
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * Check if an item or items exist in an array using "dot" notation.
+ *
+ * @param array|\ArrayAccess $array
+ * @param null|array|string $keys
+ */
+ public static function has($array, $keys): bool
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+ $keys = (array) $keys;
+ if (! $array) {
+ return false;
+ }
+ if ($keys === []) {
+ return false;
+ }
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+ if (static::exists($array, $key)) {
+ continue;
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Determines if an array is associative.
+ * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+ */
+ public static function isAssoc(array $array): bool
+ {
+ $keys = array_keys($array);
+ return array_keys($keys) !== $keys;
+ }
+
+ /**
+ * Get a subset of the items from the given array.
+ *
+ * @param array|string $keys
+ */
+ public static function only(array $array, $keys): array
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * Pluck an array of values from an array.
+ *
+ * @param array|string $value
+ * @param null|array|string $key
+ */
+ public static function pluck(array $array, $value, $key = null): array
+ {
+ $results = [];
+ [$value, $key] = static::explodePluckParameters($value, $key);
+ foreach ($array as $item) {
+ $itemValue = data_get($item, $value);
+ // If the key is "null", we will just append the value to the array and keep
+ // looping. Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+ $results[$itemKey] = $itemValue;
+ }
+ }
+ return $results;
+ }
+
+ /**
+ * Push an item onto the beginning of an array.
+ *
+ * @template TKey of array-key
+ * @template TValue
+ *
+ * @param array $array
+ * @param null|TKey $key
+ * @param TValue $value
+ * @return array
+ */
+ public static function prepend(array $array, $value, $key = null): array
+ {
+ if (is_null($key)) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+ return $array;
+ }
+
+ /**
+ * Get a value from the array, and remove it.
+ *
+ * @param null|mixed $default
+ */
+ public static function pull(array &$array, string $key, $default = null)
+ {
+ $value = static::get($array, $key, $default);
+ static::forget($array, $key);
+ return $value;
+ }
+
+ /**
+ * Get one or a specified number of random values from an array.
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function random(array $array, int $number = null)
+ {
+ $requested = is_null($number) ? 1 : $number;
+ $count = count($array);
+ if ($requested > $count) {
+ throw new InvalidArgumentException("You requested {$requested} items, but there are only {$count} items available.");
+ }
+ if (is_null($number)) {
+ return $array[array_rand($array)];
+ }
+ if ((int) $number === 0) {
+ return [];
+ }
+ $keys = array_rand($array, $number);
+ $results = [];
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ * If no key is given to the method, the entire array will be replaced.
+ *
+ * @param null|int|string $key
+ * @param mixed $value
+ */
+ public static function set(array &$array, $key, $value): array
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+ if (! is_string($key)) {
+ $array[$key] = $value;
+ return $array;
+ }
+ $keys = explode('.', $key);
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (! isset($array[$key]) || ! is_array($array[$key])) {
+ $array[$key] = [];
+ }
+ $array = &$array[$key];
+ }
+ $array[array_shift($keys)] = $value;
+ return $array;
+ }
+
+ /**
+ * Shuffle the given array and return the result.
+ */
+ public static function shuffle(array $array, int $seed = null): array
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ srand($seed);
+ usort($array, function () {
+ return rand(-1, 1);
+ });
+ }
+ return $array;
+ }
+
+ /**
+ * Sort the array using the given callback or "dot" notation.
+ *
+ * @param null|callable|string $callback
+ */
+ public static function sort(array $array, $callback = null): array
+ {
+ return Collection::make($array)->sortBy($callback)->all();
+ }
+
+ /**
+ * Recursively sort an array by keys and values.
+ */
+ public static function sortRecursive(array $array): array
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = static::sortRecursive($value);
+ }
+ }
+ if (static::isAssoc($array)) {
+ ksort($array);
+ } else {
+ sort($array);
+ }
+ return $array;
+ }
+
+ /**
+ * Convert the array into a query string.
+ */
+ public static function query(array $array): string
+ {
+ return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+ }
+
+ /**
+ * Filter the array using the given callback.
+ */
+ public static function where(array $array, callable $callback): array
+ {
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+ }
+
+ /**
+ * If the given value is not an array and not null, wrap it in one.
+ * @param mixed $value
+ */
+ public static function wrap($value): array
+ {
+ if (is_null($value)) {
+ return [];
+ }
+ return ! is_array($value) ? [$value] : $value;
+ }
+
+ /**
+ * Make array elements unique.
+ */
+ public static function unique(array $array): array
+ {
+ $result = [];
+ foreach ($array ?? [] as $key => $item) {
+ if (is_array($item)) {
+ $result[$key] = self::unique($item);
+ } else {
+ $result[$key] = $item;
+ }
+ }
+
+ if (! self::isAssoc($result)) {
+ return array_unique($result);
+ }
+
+ return $result;
+ }
+
+ public static function merge(array $array1, array $array2, bool $unique = true): array
+ {
+ $isAssoc = static::isAssoc($array1 ?: $array2);
+ if ($isAssoc) {
+ foreach ($array2 as $key => $value) {
+ if (is_array($value)) {
+ $array1[$key] = static::merge($array1[$key] ?? [], $value, $unique);
+ } else {
+ $array1[$key] = $value;
+ }
+ }
+ } else {
+ foreach ($array2 as $key => $value) {
+ if ($unique && in_array($value, $array1, true)) {
+ continue;
+ }
+ $array1[] = $value;
+ }
+
+ $array1 = array_values($array1);
+ }
+ return $array1;
+ }
+
+ /**
+ * Explode the "value" and "key" arguments passed to "pluck".
+ *
+ * @param array|string $value
+ * @param null|array|string $key
+ */
+ protected static function explodePluckParameters($value, $key): array
+ {
+ $value = is_string($value) ? explode('.', $value) : $value;
+ $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+ return [$value, $key];
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Backoff.php b/server/addons/epay/library/hyperf/utils/src/Backoff.php
new file mode 100644
index 0000000..6e486b7
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Backoff.php
@@ -0,0 +1,84 @@
+ Backoff::CAP) {
+ throw new \InvalidArgumentException(
+ sprintf(
+ 'first backoff interval must be less or equal than %d milliseconds',
+ self::CAP
+ )
+ );
+ }
+
+ $this->firstMs = $firstMs;
+ $this->currentMs = $firstMs;
+ }
+
+ /**
+ * Sleep until the next execution.
+ */
+ public function sleep(): void
+ {
+ if ($this->currentMs === 0) {
+ return;
+ }
+
+ usleep($this->currentMs * 1000);
+
+ // update backoff using Decorrelated Jitter
+ // see: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
+ $this->currentMs = rand($this->firstMs, $this->currentMs * 3);
+
+ if ($this->currentMs > self::CAP) {
+ $this->currentMs = self::CAP;
+ }
+ }
+
+ /**
+ * Get the next backoff for logging, etc.
+ * @return int next backoff
+ */
+ public function nextBackoff(): int
+ {
+ return $this->currentMs;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Channel/Caller.php b/server/addons/epay/library/hyperf/utils/src/Channel/Caller.php
new file mode 100644
index 0000000..634135e
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Channel/Caller.php
@@ -0,0 +1,79 @@
+closure = $closure;
+ $this->waitTimeout = $waitTimeout;
+ $this->initInstance();
+ }
+
+ public function call(Closure $closure)
+ {
+ $release = true;
+ $channel = $this->channel;
+ try {
+ $instance = $channel->pop($this->waitTimeout);
+ if ($instance === false) {
+ if ($channel->isClosing()) {
+ throw new ChannelClosedException('The channel was closed.');
+ }
+
+ if ($channel->isTimeout()) {
+ throw new WaitTimeoutException('The instance pop from channel timeout.');
+ }
+ }
+
+ $result = $closure($instance);
+ } catch (ChannelClosedException|WaitTimeoutException $exception) {
+ $release = false;
+ throw $exception;
+ } finally {
+ $release && $channel->push($instance ?? null);
+ }
+
+ return $result;
+ }
+
+ public function initInstance(): void
+ {
+ if ($this->channel) {
+ $this->channel->close();
+ }
+
+ $this->channel = new Channel(1);
+ $this->channel->push($this->closure->__invoke());
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Channel/ChannelManager.php b/server/addons/epay/library/hyperf/utils/src/Channel/ChannelManager.php
new file mode 100644
index 0000000..db86cf4
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Channel/ChannelManager.php
@@ -0,0 +1,72 @@
+size = $size;
+ }
+
+ public function get(int $id, bool $initialize = false): ?Channel
+ {
+ if (isset($this->channels[$id])) {
+ return $this->channels[$id];
+ }
+
+ if ($initialize) {
+ return $this->channels[$id] = $this->make($this->size);
+ }
+
+ return null;
+ }
+
+ public function make(int $limit): Channel
+ {
+ return new Channel($limit);
+ }
+
+ public function close(int $id): void
+ {
+ if ($channel = $this->channels[$id] ?? null) {
+ $channel->close();
+ }
+
+ unset($this->channels[$id]);
+ }
+
+ public function getChannels(): array
+ {
+ return $this->channels;
+ }
+
+ public function flush(): void
+ {
+ $channels = $this->getChannels();
+ foreach ($channels as $id => $channel) {
+ $this->close($id);
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/ChannelPool.php b/server/addons/epay/library/hyperf/utils/src/ChannelPool.php
new file mode 100644
index 0000000..adc6f8e
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/ChannelPool.php
@@ -0,0 +1,38 @@
+isEmpty() ? new Channel(1) : $this->pop();
+ }
+
+ public function release(Channel $channel)
+ {
+ $channel->errCode = 0;
+ $this->push($channel);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/ClearStatCache.php b/server/addons/epay/library/hyperf/utils/src/ClearStatCache.php
new file mode 100644
index 0000000..ca97023
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/ClearStatCache.php
@@ -0,0 +1,62 @@
+ self::$interval
+ || self::$lastCleared
+ || (self::$lastCleared + self::$interval < $now)
+ ) {
+ self::forceClear($filename);
+ self::$lastCleared = $now;
+ }
+ }
+
+ public static function forceClear(?string $filename = null): void
+ {
+ if ($filename !== null) {
+ clearstatcache(true, $filename);
+ } else {
+ clearstatcache();
+ }
+ }
+
+ public static function getInterval(): int
+ {
+ return self::$interval;
+ }
+
+ public static function setInterval(int $interval)
+ {
+ self::$interval = $interval;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/CodeGen/Package.php b/server/addons/epay/library/hyperf/utils/src/CodeGen/Package.php
new file mode 100644
index 0000000..50d0ef4
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/CodeGen/Package.php
@@ -0,0 +1,26 @@
+ 'bool',
+ 'boolean' => 'bool',
+ 'string' => 'string',
+ 'int' => 'int',
+ 'integer' => 'int',
+ 'float' => 'float',
+ 'double' => 'float',
+ 'array' => 'array',
+ 'object' => 'object',
+ 'callable' => 'callable',
+ 'resource' => 'resource',
+ 'mixed' => 'mixed',
+ 'iterable' => 'iterable',
+ ];
+
+ /**
+ * @var null|PhpDocReader
+ */
+ protected static $instance;
+
+ /** @var UseStatementParser */
+ private $parser;
+
+ /** @var bool */
+ private $ignorePhpDocErrors;
+
+ /**
+ * @param bool $ignorePhpDocErrors enable or disable throwing errors when PhpDoc errors occur (when parsing annotations)
+ */
+ public function __construct(bool $ignorePhpDocErrors = false)
+ {
+ $this->parser = new UseStatementParser();
+ $this->ignorePhpDocErrors = $ignorePhpDocErrors;
+ }
+
+ public static function getInstance(): PhpDocReader
+ {
+ if (static::$instance) {
+ return static::$instance;
+ }
+ return static::$instance = new static();
+ }
+
+ /**
+ * Parse the docblock of the property to get the type (class or primitive type) of the param annotation.
+ *
+ * @throws AnnotationException
+ */
+ public function getReturnType(ReflectionMethod $method, bool $withoutNamespace = false): array
+ {
+ return $this->readReturnClass($method, true, $withoutNamespace);
+ }
+
+ /**
+ * Parse the docblock of the property to get the class of the param annotation.
+ *
+ * @throws AnnotationException
+ */
+ public function getReturnClass(ReflectionMethod $method, bool $withoutNamespace = false): array
+ {
+ return $this->readReturnClass($method, false, $withoutNamespace);
+ }
+
+ protected function readReturnClass(ReflectionMethod $method, bool $allowPrimitiveTypes, bool $withoutNamespace = false): array
+ {
+ // Use reflection
+ $returnType = $method->getReturnType();
+ if ($returnType instanceof \ReflectionNamedType) {
+ if (! $returnType->isBuiltin() || $allowPrimitiveTypes) {
+ return [($returnType->allowsNull() ? '?' : '') . $returnType->getName()];
+ }
+ }
+
+ $docComment = $method->getDocComment();
+ if (! $docComment) {
+ return ['mixed'];
+ }
+ if (preg_match('/@return\s+([^\s]+)\s+/', $docComment, $matches)) {
+ [, $type] = $matches;
+ } else {
+ return ['mixed'];
+ }
+
+ $result = [];
+ $class = $method->getDeclaringClass();
+ $types = explode('|', $type);
+ foreach ($types as $type) {
+ // Ignore primitive types
+ if (isset(self::PRIMITIVE_TYPES[$type])) {
+ if ($allowPrimitiveTypes) {
+ $result[] = self::PRIMITIVE_TYPES[$type];
+ }
+ continue;
+ }
+
+ // Ignore types containing special characters ([], <> ...)
+ if (! preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) {
+ continue;
+ }
+
+ // If the class name is not fully qualified (i.e. doesn't start with a \)
+ if ($type[0] !== '\\' && ! $withoutNamespace) {
+ // Try to resolve the FQN using the class context
+ $resolvedType = $this->tryResolveFqn($type, $class, $method);
+
+ if (! $resolvedType && ! $this->ignorePhpDocErrors) {
+ throw new AnnotationException(sprintf(
+ 'The @return annotation for parameter "%s" of %s::%s contains a non existent class "%s". '
+ . 'Did you maybe forget to add a "use" statement for this annotation?',
+ $method,
+ $class->name,
+ $method->name,
+ $type
+ ));
+ }
+
+ $type = $resolvedType;
+ }
+
+ if (! $this->ignorePhpDocErrors && ! $withoutNamespace && ! $this->classExists($type)) {
+ throw new AnnotationException(sprintf(
+ 'The @return annotation for parameter "%s" of %s::%s contains a non existent class "%s"',
+ $method,
+ $class->name,
+ $method->name,
+ $type
+ ));
+ }
+
+ // Remove the leading \ (FQN shouldn't contain it)
+ $result[] = is_string($type) ? ltrim($type, '\\') : null;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Attempts to resolve the FQN of the provided $type based on the $class and $member context.
+ *
+ * @return null|string Fully qualified name of the type, or null if it could not be resolved
+ */
+ protected function tryResolveFqn(string $type, ReflectionClass $class, Reflector $member): ?string
+ {
+ $alias = ($pos = strpos($type, '\\')) === false ? $type : substr($type, 0, $pos);
+ $loweredAlias = strtolower($alias);
+
+ // Retrieve "use" statements
+ $uses = $this->parser->parseUseStatements($class);
+
+ if (isset($uses[$loweredAlias])) {
+ // Imported classes
+ if ($pos !== false) {
+ return $uses[$loweredAlias] . substr($type, $pos);
+ }
+ return $uses[$loweredAlias];
+ }
+
+ if ($this->classExists($class->getNamespaceName() . '\\' . $type)) {
+ return $class->getNamespaceName() . '\\' . $type;
+ }
+
+ if (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) {
+ // Class namespace
+ return $uses['__NAMESPACE__'] . '\\' . $type;
+ }
+
+ if ($this->classExists($type)) {
+ // No namespace
+ return $type;
+ }
+
+ // If all fail, try resolving through related traits
+ return $this->tryResolveFqnInTraits($type, $class, $member);
+ }
+
+ /**
+ * Attempts to resolve the FQN of the provided $type based on the $class and $member context, specifically searching
+ * through the traits that are used by the provided $class.
+ *
+ * @return null|string Fully qualified name of the type, or null if it could not be resolved
+ */
+ protected function tryResolveFqnInTraits(string $type, ReflectionClass $class, Reflector $member): ?string
+ {
+ /** @var ReflectionClass[] $traits */
+ $traits = [];
+
+ // Get traits for the class and its parents
+ while ($class) {
+ $traits = array_merge($traits, $class->getTraits());
+ $class = $class->getParentClass();
+ }
+
+ foreach ($traits as $trait) {
+ // Eliminate traits that don't have the property/method/parameter
+ if ($member instanceof ReflectionProperty && ! $trait->hasProperty($member->name)) {
+ continue;
+ }
+ if ($member instanceof ReflectionMethod && ! $trait->hasMethod($member->name)) {
+ continue;
+ }
+ if ($member instanceof ReflectionParameter && ! $trait->hasMethod($member->getDeclaringFunction()->name)) {
+ continue;
+ }
+
+ // Run the resolver again with the ReflectionClass instance for the trait
+ $resolvedType = $this->tryResolveFqn($type, $trait, $member);
+
+ if ($resolvedType) {
+ return $resolvedType;
+ }
+ }
+
+ return null;
+ }
+
+ protected function classExists(string $class): bool
+ {
+ return class_exists($class) || interface_exists($class);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/CodeGen/PhpDocReaderManager.php b/server/addons/epay/library/hyperf/utils/src/CodeGen/PhpDocReaderManager.php
new file mode 100644
index 0000000..3b6971f
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/CodeGen/PhpDocReaderManager.php
@@ -0,0 +1,30 @@
+parser = $parserFactory->create(ParserFactory::ONLY_PHP7);
+ }
+
+ public static function getInstance(): PhpParser
+ {
+ if (static::$instance) {
+ return static::$instance;
+ }
+ return static::$instance = new static();
+ }
+
+ /**
+ * @return null|Node\Stmt[]
+ */
+ public function getNodesFromReflectionClass(ReflectionClass $reflectionClass): ?array
+ {
+ $code = file_get_contents($reflectionClass->getFileName());
+ return $this->parser->parse($code);
+ }
+
+ public function getNodeFromReflectionParameter(ReflectionParameter $parameter): Node\Param
+ {
+ $result = new Node\Param(
+ new Node\Expr\Variable($parameter->getName())
+ );
+
+ if ($parameter->isDefaultValueAvailable()) {
+ $result->default = $this->getExprFromValue($parameter->getDefaultValue());
+ }
+
+ if ($parameter->hasType()) {
+ /** @var \ReflectionNamedType|\ReflectionUnionType $reflection */
+ $reflection = $parameter->getType();
+ if ($reflection instanceof \ReflectionUnionType) {
+ $unionType = [];
+ foreach ($reflection->getTypes() as $objType) {
+ $type = $objType->getName();
+ if (! in_array($type, static::TYPES)) {
+ $unionType[] = new Node\Name('\\' . $type);
+ } else {
+ $unionType[] = new Node\Identifier($type);
+ }
+ }
+ $result->type = new Node\UnionType($unionType);
+ } else {
+ $type = $reflection->getName();
+ if (! in_array($type, static::TYPES)) {
+ $result->type = new Node\Name('\\' . $type);
+ } else {
+ $result->type = new Node\Identifier($type);
+ }
+ }
+ }
+
+ if ($parameter->isPassedByReference()) {
+ $result->byRef = true;
+ }
+
+ if ($parameter->isVariadic()) {
+ $result->variadic = true;
+ }
+
+ return $result;
+ }
+
+ public function getExprFromValue($value): Node\Expr
+ {
+ switch (gettype($value)) {
+ case 'array':
+ return new Node\Expr\Array_($value);
+ case 'string':
+ return new Node\Scalar\String_($value);
+ case 'integer':
+ return new Node\Scalar\LNumber($value);
+ case 'double':
+ return new Node\Scalar\DNumber($value);
+ case 'NULL':
+ return new Node\Expr\ConstFetch(new Node\Name('null'));
+ case 'boolean':
+ return new Node\Expr\ConstFetch(new Node\Name($value ? 'true' : 'false'));
+ default:
+ throw new InvalidArgumentException($value . ' is invalid');
+ }
+ }
+
+ /**
+ * @return Node\Stmt\ClassMethod[]
+ */
+ public function getAllMethodsFromStmts(array $stmts): array
+ {
+ $methods = [];
+ foreach ($stmts as $namespace) {
+ if (! $namespace instanceof Node\Stmt\Namespace_) {
+ continue;
+ }
+
+ foreach ($namespace->stmts as $class) {
+ if (! $class instanceof Node\Stmt\Class_ && ! $class instanceof Node\Stmt\Interface_) {
+ continue;
+ }
+
+ foreach ($class->getMethods() as $method) {
+ $methods[] = $method;
+ }
+ }
+ }
+
+ return $methods;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/CodeGen/Project.php b/server/addons/epay/library/hyperf/utils/src/CodeGen/Project.php
new file mode 100644
index 0000000..4aa5937
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/CodeGen/Project.php
@@ -0,0 +1,69 @@
+getAutoloadRules() as $prefix => $prefixPath) {
+ if ($this->isRootNamespace($prefix) || strpos($path, $prefixPath) === 0) {
+ return $prefix . str_replace('/', '\\', substr($path, strlen($prefixPath)));
+ }
+ }
+ throw new \RuntimeException("Invalid project path: {$path}");
+ }
+
+ public function className(string $path): string
+ {
+ return $this->namespace($path);
+ }
+
+ public function path(string $name, $extension = '.php'): string
+ {
+ if (Str::endsWith($name, '\\')) {
+ $extension = '';
+ }
+
+ foreach ($this->getAutoloadRules() as $prefix => $prefixPath) {
+ if ($this->isRootNamespace($prefix) || strpos($name, $prefix) === 0) {
+ return $prefixPath . str_replace('\\', '/', substr($name, strlen($prefix))) . $extension;
+ }
+ }
+
+ throw new \RuntimeException("Invalid class name: {$name}");
+ }
+
+ protected function isRootNamespace(string $namespace): bool
+ {
+ return $namespace === '';
+ }
+
+ protected function getAutoloadRules(): array
+ {
+ return data_get(Composer::getJsonContent(), 'autoload.psr-4', []);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Codec/Base62.php b/server/addons/epay/library/hyperf/utils/src/Codec/Base62.php
new file mode 100644
index 0000000..8007238
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Codec/Base62.php
@@ -0,0 +1,39 @@
+ 0) {
+ $remain = $number % self::BASE;
+ $chars[] = self::CHARS[$remain];
+ $number = ($number - $remain) / self::BASE;
+ }
+ return implode('', array_reverse($chars));
+ }
+
+ public static function decode(string $data): int
+ {
+ return array_reduce(array_map(function ($character) {
+ return strpos(self::CHARS, $character);
+ }, str_split($data)), function ($result, $remain) {
+ return $result * self::BASE + $remain;
+ });
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Codec/Json.php b/server/addons/epay/library/hyperf/utils/src/Codec/Json.php
new file mode 100644
index 0000000..0e3d8d5
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Codec/Json.php
@@ -0,0 +1,56 @@
+toArray();
+ }
+
+ try {
+ $json = json_encode($data, $flags | JSON_THROW_ON_ERROR, $depth);
+ } catch (\Throwable $exception) {
+ throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
+ }
+
+ return $json;
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ public static function decode(string $json, bool $assoc = true, int $depth = 512, int $flags = 0)
+ {
+ try {
+ $decode = json_decode($json, $assoc, $depth, $flags | JSON_THROW_ON_ERROR);
+ } catch (\Throwable $exception) {
+ throw new InvalidArgumentException($exception->getMessage(), $exception->getCode());
+ }
+
+ return $decode;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Codec/Xml.php b/server/addons/epay/library/hyperf/utils/src/Codec/Xml.php
new file mode 100644
index 0000000..4a26fbf
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Codec/Xml.php
@@ -0,0 +1,70 @@
+toArray();
+ } else {
+ $data = (array) $data;
+ }
+ if ($parentNode === null) {
+ $xml = new SimpleXMLElement('' . "<{$root}>{$root}>");
+ } else {
+ $xml = $parentNode;
+ }
+ foreach ($data as $key => $value) {
+ if (is_array($value)) {
+ self::toXml($value, $xml->addChild($key));
+ } else {
+ if (is_numeric($key)) {
+ $xml->addChild('item' . $key, (string) $value);
+ } else {
+ $xml->addChild($key, (string) $value);
+ }
+ }
+ }
+ return trim($xml->asXML());
+ }
+
+ public static function toArray($xml)
+ {
+ // For PHP 8.0, libxml_disable_entity_loader() has been deprecated.
+ // As libxml 2.9.0 is now required, external entity loading is guaranteed to be disabled by default.
+ // And this function is no longer needed to protect against XXE attacks, unless the (still vulnerable). LIBXML_NOENT is used.
+ // In that case, it is recommended to refactor the code using libxml_set_external_entity_loader() to suppress loading of external entities.
+ if (\PHP_VERSION_ID < 80000) {
+ $disableLibxmlEntityLoader = libxml_disable_entity_loader(true);
+ $respObject = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOERROR);
+ libxml_disable_entity_loader($disableLibxmlEntityLoader);
+ } else {
+ $respObject = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOERROR);
+ }
+
+ if ($respObject === false) {
+ throw new InvalidArgumentException('Syntax error.');
+ }
+
+ return json_decode(json_encode($respObject), true);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Collection.php b/server/addons/epay/library/hyperf/utils/src/Collection.php
new file mode 100644
index 0000000..e696e11
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Collection.php
@@ -0,0 +1,1835 @@
+
+ * @implements Arrayable
+ * @implements IteratorAggregate
+ *
+ * @property HigherOrderCollectionProxy $average
+ * @property HigherOrderCollectionProxy $avg
+ * @property HigherOrderCollectionProxy $contains
+ * @property HigherOrderCollectionProxy $each
+ * @property HigherOrderCollectionProxy $every
+ * @property HigherOrderCollectionProxy $filter
+ * @property HigherOrderCollectionProxy $first
+ * @property HigherOrderCollectionProxy $flatMap
+ * @property HigherOrderCollectionProxy $groupBy
+ * @property HigherOrderCollectionProxy $keyBy
+ * @property HigherOrderCollectionProxy $map
+ * @property HigherOrderCollectionProxy $max
+ * @property HigherOrderCollectionProxy $min
+ * @property HigherOrderCollectionProxy $partition
+ * @property HigherOrderCollectionProxy $reject
+ * @property HigherOrderCollectionProxy $sortBy
+ * @property HigherOrderCollectionProxy $sortByDesc
+ * @property HigherOrderCollectionProxy $sum
+ * @property HigherOrderCollectionProxy $unique
+ */
+class Collection implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
+{
+ use Macroable;
+
+ /**
+ * The items contained in the collection.
+ *
+ * @var array
+ */
+ protected $items = [];
+
+ /**
+ * The methods that can be proxied.
+ *
+ * @var string[]
+ */
+ protected static $proxies
+ = [
+ 'average',
+ 'avg',
+ 'contains',
+ 'each',
+ 'every',
+ 'filter',
+ 'first',
+ 'flatMap',
+ 'groupBy',
+ 'keyBy',
+ 'map',
+ 'max',
+ 'min',
+ 'partition',
+ 'reject',
+ 'sortBy',
+ 'sortByDesc',
+ 'sum',
+ 'unique',
+ ];
+
+ /**
+ * Create a new collection.
+ * @param null|iterable|Jsonable|JsonSerializable $items
+ */
+ public function __construct($items = [])
+ {
+ $this->items = $this->getArrayableItems($items);
+ }
+
+ /**
+ * Convert the collection to its string representation.
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * Dynamically access collection proxies.
+ *
+ * @throws \Exception
+ */
+ public function __get(string $key)
+ {
+ if (! in_array($key, static::$proxies)) {
+ throw new Exception("Property [{$key}] does not exist on this collection instance.");
+ }
+ return new HigherOrderCollectionProxy($this, $key);
+ }
+
+ /**
+ * @param null|iterable|Jsonable|JsonSerializable $items
+ * @return static
+ */
+ public function fill($items = [])
+ {
+ $this->items = $this->getArrayableItems($items);
+ return $this;
+ }
+
+ /**
+ * Create a new collection instance if the value isn't one already.
+ *
+ * @template TMakeKey of array-key
+ * @template TMakeValue
+ *
+ * @param null|Arrayable|iterable|Jsonable|JsonSerializable $items
+ * @return static
+ */
+ public static function make($items = []): self
+ {
+ return new static($items);
+ }
+
+ /**
+ * Wrap the given value in a collection if applicable.
+ *
+ * @template TWrapKey of array-key
+ * @template TWrapValue
+ *
+ * @param iterable $value
+ * @return static
+ */
+ public static function wrap($value): self
+ {
+ return $value instanceof self ? new static($value) : new static(Arr::wrap($value));
+ }
+
+ /**
+ * Get the underlying items from the given collection if applicable.
+ *
+ * @template TUnwrapKey of array-key
+ * @template TUnwrapValue
+ *
+ * @param array|static $value
+ * @return array
+ */
+ public static function unwrap($value): array
+ {
+ return $value instanceof self ? $value->all() : $value;
+ }
+
+ /**
+ * Create a new collection by invoking the callback a given amount of times.
+ *
+ * @template TTimesValue
+ *
+ * @param (callable(int): TTimesValue)|null $callback
+ * @return static
+ */
+ public static function times(int $number, callable $callback = null): self
+ {
+ if ($number < 1) {
+ return new static();
+ }
+ if (is_null($callback)) {
+ return new static(range(1, $number));
+ }
+ return (new static(range(1, $number)))->map($callback);
+ }
+
+ /**
+ * Get all of the items in the collection.
+ *
+ * @return array
+ */
+ public function all(): array
+ {
+ return $this->items;
+ }
+
+ /**
+ * Get the average value of a given key.
+ *
+ * @param (callable(TValue): float|int)|string|null $callback
+ */
+ public function avg($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+ $items = $this->map(function ($value) use ($callback) {
+ return $callback($value);
+ })->filter(function ($value) {
+ return ! is_null($value);
+ });
+ if ($count = $items->count()) {
+ return $items->sum() / $count;
+ }
+ }
+
+ /**
+ * Alias for the "avg" method.
+ *
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return null|float|int
+ */
+ public function average($callback = null)
+ {
+ return $this->avg($callback);
+ }
+
+ /**
+ * Get the median of a given key.
+ *
+ * @param null|array|string $key
+ */
+ public function median($key = null)
+ {
+ $values = (isset($key) ? $this->pluck($key) : $this)->filter(function ($item) {
+ return ! is_null($item);
+ })->sort()->values();
+ $count = $values->count();
+ if ($count == 0) {
+ return;
+ }
+ $middle = (int) ($count / 2);
+ if ($count % 2) {
+ return $values->get($middle);
+ }
+ return (new static([
+ $values->get($middle - 1),
+ $values->get($middle),
+ ]))->average();
+ }
+
+ /**
+ * Get the mode of a given key.
+ *
+ * @param null|array|string $key
+ * @return null|array
+ */
+ public function mode($key = null)
+ {
+ if ($this->count() == 0) {
+ return null;
+ }
+ $collection = isset($key) ? $this->pluck($key) : $this;
+
+ /**
+ * @template TValue of array-key
+ * @var static $counts
+ */
+ $counts = new self();
+ $collection->each(function ($value) use ($counts) {
+ $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
+ });
+ $sorted = $counts->sort();
+ $highestValue = $sorted->last();
+ return $sorted->filter(function ($value) use ($highestValue) {
+ return $value == $highestValue;
+ })->sort()->keys()->all();
+ }
+
+ /**
+ * Collapse the collection of items into a single array.
+ *
+ * @return static
+ */
+ public function collapse(): self
+ {
+ return new static(Arr::collapse($this->items));
+ }
+
+ /**
+ * Determine if an item exists in the collection.
+ *
+ * @param null|mixed $operator
+ * @param null|mixed $value
+ * @param (callable(TValue): bool)|TValue|string $key
+ */
+ public function contains($key, $operator = null, $value = null): bool
+ {
+ if (func_num_args() === 1) {
+ if ($this->useAsCallable($key)) {
+ $placeholder = new stdClass();
+ return $this->first($key, $placeholder) !== $placeholder;
+ }
+ return in_array($key, $this->items);
+ }
+ return $this->contains($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Determine if an item exists in the collection using strict comparison.
+ *
+ * @param null|TValue $value
+ * @param callable|TKey|TValue $key
+ */
+ public function containsStrict($key, $value = null): bool
+ {
+ if (func_num_args() === 2) {
+ return $this->contains(function ($item) use ($key, $value) {
+ return data_get($item, $key) === $value;
+ });
+ }
+ if ($this->useAsCallable($key)) {
+ return ! is_null($this->first($key));
+ }
+ return in_array($key, $this->items, true);
+ }
+
+ /**
+ * Cross join with the given lists, returning all possible permutations.
+ */
+ public function crossJoin(...$lists): self
+ {
+ return new static(Arr::crossJoin($this->items, ...array_map([$this, 'getArrayableItems'], $lists)));
+ }
+
+ /**
+ * Dump the collection and end the script.
+ */
+ public function dd(...$args): void
+ {
+ call_user_func_array([$this, 'dump'], $args);
+ exit(1);
+ }
+
+ /**
+ * Dump the collection.
+ */
+ public function dump(): self
+ {
+ $params = (new static(func_get_args()));
+ $params->push($this)->each(function ($item) {
+ if (! class_exists(VarDumper::class)) {
+ throw new \RuntimeException('symfony/var-dumper package required, please require the package via "composer require symfony/var-dumper"');
+ }
+ VarDumper::dump($item);
+ });
+ return $this;
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function diff($items): self
+ {
+ return new static(array_diff($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @param callable(TValue): int $callback
+ * @return static
+ */
+ public function diffUsing($items, callable $callback): self
+ {
+ return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function diffAssoc($items): self
+ {
+ return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @param callable(TKey): int $callback
+ * @return static
+ */
+ public function diffAssocUsing($items, callable $callback): self
+ {
+ return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function diffKeys($items): self
+ {
+ return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @param callable(TKey): int $callback
+ * @return static
+ */
+ public function diffKeysUsing($items, callable $callback): self
+ {
+ return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Execute a callback over each item.
+ * @param callable(TValue,TKey): mixed $callback
+ */
+ public function each(callable $callback): self
+ {
+ foreach ($this->items as $key => $item) {
+ if ($callback($item, $key) === false) {
+ break;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Execute a callback over each nested chunk of items.
+ * @param callable(...mixed): mixed $callback
+ * @return static
+ */
+ public function eachSpread(callable $callback): self
+ {
+ return $this->each(function ($chunk, $key) use ($callback) {
+ $chunk[] = $key;
+ return $callback(...$chunk);
+ });
+ }
+
+ /**
+ * Determine if all items in the collection pass the given test.
+ *
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ */
+ public function every($key, $operator = null, $value = null): bool
+ {
+ if (func_num_args() === 1) {
+ $callback = $this->valueRetriever($key);
+ foreach ($this->items as $k => $v) {
+ if (! $callback($v, $k)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return $this->every($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param array|static $keys
+ * @return static
+ */
+ public function except($keys): self
+ {
+ if ($keys instanceof self) {
+ $keys = $keys->all();
+ } elseif (! is_array($keys)) {
+ $keys = func_get_args();
+ }
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ /**
+ * Run a filter over each of the items.
+ *
+ * @param callable(TValue, TKey): bool|null $callback
+ * @return static
+ */
+ public function filter(callable $callback = null): self
+ {
+ if ($callback) {
+ return new static(Arr::where($this->items, $callback));
+ }
+ return new static(array_filter($this->items));
+ }
+
+ /**
+ * Apply the callback if the value is truthy.
+ *
+ * @param callable($this): $this $callback
+ * @param callable($this): $this $default
+ * @return $this
+ */
+ public function when(bool $value, callable $callback, callable $default = null): self
+ {
+ if ($value) {
+ return $callback($this, $value);
+ }
+ if ($default) {
+ return $default($this, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Apply the callback if the value is falsy.
+ *
+ * @param callable($this): $this $callback
+ * @param callable($this): $this|null $default
+ * @return $this
+ */
+ public function unless(bool $value, callable $callback, callable $default = null): self
+ {
+ return $this->when(! $value, $callback, $default);
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param mixed $operator
+ * @param mixed $value
+ * @return static
+ */
+ public function where(string $key, $operator = null, $value = null): self
+ {
+ return $this->filter($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function whereStrict(string $key, $value): self
+ {
+ return $this->where($key, '===', $value);
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param Arrayable|iterable $values
+ * @return static
+ */
+ public function whereIn(string $key, $values, bool $strict = false): self
+ {
+ $values = $this->getArrayableItems($values);
+ return $this->filter(function ($item) use ($key, $values, $strict) {
+ return in_array(data_get($item, $key), $values, $strict);
+ });
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param Arrayable|iterable $values
+ * @return static
+ */
+ public function whereInStrict(string $key, $values): self
+ {
+ return $this->whereIn($key, $values, true);
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param Arrayable|iterable $values
+ * @return static
+ */
+ public function whereNotIn(string $key, $values, bool $strict = false): self
+ {
+ $values = $this->getArrayableItems($values);
+ return $this->reject(function ($item) use ($key, $values, $strict) {
+ return in_array(data_get($item, $key), $values, $strict);
+ });
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param Arrayable|iterable $values
+ * @return static
+ */
+ public function whereNotInStrict(string $key, $values): self
+ {
+ return $this->whereNotIn($key, $values, true);
+ }
+
+ /**
+ * Filter the items, removing any items that don't match the given type.
+ *
+ * @param class-string $type
+ * @return static
+ */
+ public function whereInstanceOf(string $type): self
+ {
+ return $this->filter(function ($value) use ($type) {
+ return $value instanceof $type;
+ });
+ }
+
+ /**
+ * Get the first item from the collection.
+ *
+ * @template TFirstDefault
+ *
+ * @param callable(TValue, TKey): bool|null $callback
+ * @param TFirstDefault|callable(): TFirstDefault $default
+ * @return TFirstDefault|TValue
+ */
+ public function first(callable $callback = null, $default = null)
+ {
+ return Arr::first($this->items, $callback, $default);
+ }
+
+ /**
+ * Get the first item by the given key value pair.
+ *
+ * @param mixed $operator
+ * @param mixed $value
+ * @return null|TValue
+ */
+ public function firstWhere(string $key, $operator, $value = null)
+ {
+ return $this->first($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Get a flattened array of the items in the collection.
+ *
+ * @param float|int $depth
+ * @return static
+ */
+ public function flatten($depth = INF): self
+ {
+ return new static(Arr::flatten($this->items, $depth));
+ }
+
+ /**
+ * Flip the items in the collection.
+ *
+ * @return static
+ */
+ public function flip(): self
+ {
+ return new static(array_flip($this->items));
+ }
+
+ /**
+ * Remove an item from the collection by key.
+ *
+ * @param array|TKey $keys
+ * @return $this
+ */
+ public function forget($keys): self
+ {
+ foreach ((array) $keys as $key) {
+ $this->offsetUnset($key);
+ }
+ return $this;
+ }
+
+ /**
+ * Get an item from the collection by key.
+ *
+ * @template TGetDefault
+ *
+ * @param TKey $key
+ * @param TGetDefault|(\Closure(): TGetDefault) $default
+ * @return TGetDefault|TValue
+ */
+ public function get($key, $default = null)
+ {
+ if ($this->offsetExists($key)) {
+ return $this->items[$key];
+ }
+ return value($default);
+ }
+
+ /**
+ * Group an associative array by a field or using a callback.
+ * @param mixed $groupBy
+ */
+ public function groupBy($groupBy, bool $preserveKeys = false): self
+ {
+ if (is_array($groupBy)) {
+ $nextGroups = $groupBy;
+ $groupBy = array_shift($nextGroups);
+ }
+ $groupBy = $this->valueRetriever($groupBy);
+ $results = [];
+ foreach ($this->items as $key => $value) {
+ $groupKeys = $groupBy($value, $key);
+ if (! is_array($groupKeys)) {
+ $groupKeys = [$groupKeys];
+ }
+ foreach ($groupKeys as $groupKey) {
+ $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
+ if (! array_key_exists($groupKey, $results)) {
+ $results[$groupKey] = new static();
+ }
+ $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
+ }
+ }
+ $result = new static($results);
+ if (! empty($nextGroups)) {
+ return $result->map->groupBy($nextGroups, $preserveKeys);
+ }
+ return $result;
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param (callable(TValue, TKey): array-key)|array|string $keyBy
+ * @return static>
+ */
+ public function keyBy($keyBy): self
+ {
+ $keyBy = $this->valueRetriever($keyBy);
+ $results = [];
+ foreach ($this->items as $key => $item) {
+ $resolvedKey = $keyBy($item, $key);
+ if (is_object($resolvedKey)) {
+ $resolvedKey = (string) $resolvedKey;
+ }
+ $results[$resolvedKey] = $item;
+ }
+ return new static($results);
+ }
+
+ /**
+ * Determine if an item exists in the collection by key.
+ * @param array|TKey $key
+ */
+ public function has($key): bool
+ {
+ $keys = is_array($key) ? $key : func_get_args();
+ foreach ($keys as $value) {
+ if (! $this->offsetExists($value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Concatenate values of a given key as a string.
+ */
+ public function implode(string $value, string $glue = null): string
+ {
+ $first = $this->first();
+ if (is_array($first) || is_object($first)) {
+ return implode($glue, $this->pluck($value)->all());
+ }
+ return implode($value, $this->items);
+ }
+
+ /**
+ * Intersect the collection with the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function intersect($items): self
+ {
+ return new static(array_intersect($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Intersect the collection with the given items by key.
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function intersectByKeys($items): self
+ {
+ return new static(array_intersect_key($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Determine if the collection is empty or not.
+ */
+ public function isEmpty(): bool
+ {
+ return empty($this->items);
+ }
+
+ /**
+ * Determine if the collection is not empty.
+ */
+ public function isNotEmpty(): bool
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * Get the keys of the collection items.
+ * @return static
+ */
+ public function keys(): self
+ {
+ return new static(array_keys($this->items));
+ }
+
+ /**
+ * Get the last item from the collection.
+ *
+ * @template TLastDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TLastDefault|(\Closure(): TLastDefault) $default
+ * @return TLastDefault|TValue
+ */
+ public function last(callable $callback = null, $default = null)
+ {
+ return Arr::last($this->items, $callback, $default);
+ }
+
+ /**
+ * Get the values of a given key.
+ *
+ * @param array|string $value
+ * @return static
+ */
+ public function pluck($value, ?string $key = null): self
+ {
+ return new static(Arr::pluck($this->items, $value, $key));
+ }
+
+ /**
+ * Run a map over each of the items.
+ *
+ * @template TMapValue
+ *
+ * @param callable(TValue, TKey): TMapValue $callback
+ * @return static
+ */
+ public function map(callable $callback): self
+ {
+ $keys = array_keys($this->items);
+ $items = array_map($callback, $this->items, $keys);
+ return new static(array_combine($keys, $items));
+ }
+
+ /**
+ * Run a map over each nested chunk of items.
+ *
+ * @template TMapSpreadValue
+ *
+ * @param callable(mixed): TMapSpreadValue $callback
+ * @return static
+ */
+ public function mapSpread(callable $callback): self
+ {
+ return $this->map(function ($chunk, $key) use ($callback) {
+ $chunk[] = $key;
+ return $callback(...$chunk);
+ });
+ }
+
+ /**
+ * Run a dictionary map over the items.
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @template TMapToDictionaryKey of array-key
+ * @template TMapToDictionaryValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
+ */
+ public function mapToDictionary(callable $callback): self
+ {
+ $dictionary = [];
+ foreach ($this->items as $key => $item) {
+ $pair = $callback($item, $key);
+ $key = key($pair);
+ $value = reset($pair);
+ if (! isset($dictionary[$key])) {
+ $dictionary[$key] = [];
+ }
+ $dictionary[$key][] = $value;
+ }
+ return new static($dictionary);
+ }
+
+ /**
+ * Run a grouping map over the items.
+ * The callback should return an associative array with a single key/value pair.
+ */
+ public function mapToGroups(callable $callback): self
+ {
+ $groups = $this->mapToDictionary($callback);
+ return $groups->map([$this, 'make']);
+ }
+
+ /**
+ * Run an associative map over each of the items.
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @template TMapWithKeysKey of array-key
+ * @template TMapWithKeysValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static
+ */
+ public function mapWithKeys(callable $callback): self
+ {
+ $result = [];
+ foreach ($this->items as $key => $value) {
+ $assoc = $callback($value, $key);
+ foreach ($assoc as $mapKey => $mapValue) {
+ $result[$mapKey] = $mapValue;
+ }
+ }
+ return new static($result);
+ }
+
+ /**
+ * Map a collection and flatten the result by a single level.
+ *
+ * @param callable(TValue, TKey): mixed $callback
+ * @return static
+ */
+ public function flatMap(callable $callback): self
+ {
+ return $this->map($callback)->collapse();
+ }
+
+ /**
+ * Map the values into a new class.
+ *
+ * @param class-string $class
+ * @return static
+ */
+ public function mapInto(string $class): self
+ {
+ return $this->map(function ($value, $key) use ($class) {
+ return new $class($value, $key);
+ });
+ }
+
+ /**
+ * Get the max value of a given key.
+ *
+ * @param (callable(TValue):mixed)|string|null $callback
+ * @return TValue
+ */
+ public function max($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+ return $this->filter(function ($value) {
+ return ! is_null($value);
+ })->reduce(function ($result, $item) use ($callback) {
+ $value = $callback($item);
+ return is_null($result) || $value > $result ? $value : $result;
+ });
+ }
+
+ /**
+ * Merge the collection with the given items.
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function merge($items): self
+ {
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Create a collection by using this collection for keys and another for its values.
+ *
+ * @template TCombineValue
+ *
+ * @param Arrayable|iterable $values
+ * @return static
+ */
+ public function combine($values): self
+ {
+ return new static(array_combine($this->all(), $this->getArrayableItems($values)));
+ }
+
+ /**
+ * Union the collection with the given items.
+ *
+ * @param Arrayable|iterable $items
+ * @return static
+ */
+ public function union($items): self
+ {
+ return new static($this->items + $this->getArrayableItems($items));
+ }
+
+ /**
+ * Get the min value of a given key.
+ *
+ * @param (callable(TValue):mixed)|string|null $callback
+ * @return TValue
+ */
+ public function min($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+ return $this->map(function ($value) use ($callback) {
+ return $callback($value);
+ })->filter(function ($value) {
+ return ! is_null($value);
+ })->reduce(function ($result, $value) {
+ return is_null($result) || $value < $result ? $value : $result;
+ });
+ }
+
+ /**
+ * Create a new collection consisting of every n-th element.
+ *
+ * @return static
+ */
+ public function nth(int $step, int $offset = 0): self
+ {
+ $new = [];
+ $position = 0;
+ foreach ($this->items as $item) {
+ if ($position % $step === $offset) {
+ $new[] = $item;
+ }
+ ++$position;
+ }
+ return new static($new);
+ }
+
+ /**
+ * Get the items with the specified keys.
+ *
+ * @param null|array|static|string $keys
+ * @return static
+ */
+ public function only($keys): self
+ {
+ if (is_null($keys)) {
+ return new static($this->items);
+ }
+ if ($keys instanceof self) {
+ $keys = $keys->all();
+ }
+ $keys = is_array($keys) ? $keys : func_get_args();
+ return new static(Arr::only($this->items, $keys));
+ }
+
+ /**
+ * "Paginate" the collection by slicing it into a smaller collection.
+ */
+ public function forPage(int $page, int $perPage): self
+ {
+ $offset = max(0, ($page - 1) * $perPage);
+ return $this->slice($offset, $perPage);
+ }
+
+ /**
+ * Partition the collection into two arrays using the given callback or key.
+ *
+ * @param callable(TValue, TKey) bool)|TValue|string $key
+ * @param null|string|TValue $operator
+ * @param null|TValue $value
+ * @return static>
+ */
+ public function partition($key, $operator = null, $value = null): self
+ {
+ $partitions = [new static(), new static()];
+ $callback = func_num_args() === 1 ? $this->valueRetriever($key) : $this->operatorForWhere(...func_get_args());
+ foreach ($this->items as $key => $item) {
+ $partitions[(int) ! $callback($item, $key)][$key] = $item;
+ }
+ return new static($partitions);
+ }
+
+ /**
+ * Pass the collection to the given callback and return the result.
+ *
+ * @template TPipeReturnType
+ *
+ * @param callable($this): TPipeReturnType $callback
+ * @return TPipeReturnType
+ */
+ public function pipe(callable $callback)
+ {
+ return $callback($this);
+ }
+
+ /**
+ * Get and remove the last item from the collection.
+ */
+ public function pop()
+ {
+ return array_pop($this->items);
+ }
+
+ /**
+ * Push an item onto the beginning of the collection.
+ *
+ * @param TValue $value
+ * @param null|TKey $key
+ * @return $this
+ */
+ public function prepend($value, $key = null): self
+ {
+ $this->items = Arr::prepend($this->items, $value, $key);
+ return $this;
+ }
+
+ /**
+ * Push an item onto the end of the collection.
+ *
+ * @param TValue $value
+ * @return $this
+ */
+ public function push($value): self
+ {
+ $this->offsetSet(null, $value);
+ return $this;
+ }
+
+ /**
+ * Push all of the given items onto the collection.
+ *
+ * @param iterable $source
+ * @return static
+ */
+ public function concat($source): self
+ {
+ $result = new static($this);
+ foreach ($source as $item) {
+ $result->push($item);
+ }
+ return $result;
+ }
+
+ /**
+ * Get and remove an item from the collection.
+ *
+ * @template TPullDefault
+ *
+ * @param TKey $key
+ * @param TPullDefault|(\Closure(): TPullDefault) $default
+ * @return TPullDefault|TValue
+ */
+ public function pull($key, $default = null)
+ {
+ return Arr::pull($this->items, $key, $default);
+ }
+
+ /**
+ * Put an item in the collection by key.
+ *
+ * @param TKey $key
+ * @param TValue $value
+ * @return $this
+ */
+ public function put($key, $value): self
+ {
+ $this->offsetSet($key, $value);
+ return $this;
+ }
+
+ /**
+ * Get one or a specified number of items randomly from the collection.
+ *
+ * @throws \InvalidArgumentException
+ * @return static|TValue
+ */
+ public function random(int $number = null)
+ {
+ if (is_null($number)) {
+ return Arr::random($this->items);
+ }
+ return new static(Arr::random($this->items, $number));
+ }
+
+ /**
+ * Reduce the collection to a single value.
+ *
+ * @template TReduceInitial
+ * @template TReduceReturnType
+ *
+ * @param callable(TReduceInitial|TReduceReturnType, TValue): TReduceReturnType $callback
+ * @param TReduceInitial $initial
+ * @return TReduceInitial|TReduceReturnType
+ */
+ public function reduce(callable $callback, $initial = null)
+ {
+ return array_reduce($this->items, $callback, $initial);
+ }
+
+ /**
+ * Create a collection of all elements that do not pass a given truth test.
+ *
+ * @param callable(TValue, TKey): bool|bool $callback
+ * @return static
+ */
+ public function reject($callback): self
+ {
+ if ($this->useAsCallable($callback)) {
+ return $this->filter(function ($value, $key) use ($callback) {
+ return ! $callback($value, $key);
+ });
+ }
+ return $this->filter(function ($item) use ($callback) {
+ return $item != $callback;
+ });
+ }
+
+ /**
+ * Reverse items order.
+ *
+ * @return static
+ */
+ public function reverse(): self
+ {
+ return new static(array_reverse($this->items, true));
+ }
+
+ /**
+ * Search the collection for a given value and return the corresponding key if successful.
+ *
+ * @param TValue|(callable(TValue,TKey): bool) $value
+ * @return bool|TKey
+ */
+ public function search($value, bool $strict = false)
+ {
+ if (! $this->useAsCallable($value)) {
+ return array_search($value, $this->items, $strict);
+ }
+ foreach ($this->items as $key => $item) {
+ if (call_user_func($value, $item, $key)) {
+ return $key;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get and remove the first item from the collection.
+ *
+ * @return null|TValue
+ */
+ public function shift()
+ {
+ return array_shift($this->items);
+ }
+
+ /**
+ * Shuffle the items in the collection.
+ *
+ * @return static
+ */
+ public function shuffle(int $seed = null): self
+ {
+ return new static(Arr::shuffle($this->items, $seed));
+ }
+
+ /**
+ * Slice the underlying collection array.
+ *
+ * @return static
+ */
+ public function slice(int $offset, int $length = null): self
+ {
+ return new static(array_slice($this->items, $offset, $length, true));
+ }
+
+ /**
+ * Split a collection into a certain number of groups.
+ *
+ * @return static>
+ */
+ public function split(int $numberOfGroups): self
+ {
+ if ($this->isEmpty()) {
+ return new static();
+ }
+ $groups = new static();
+ $groupSize = (int) floor($this->count() / $numberOfGroups);
+ $remain = $this->count() % $numberOfGroups;
+ $start = 0;
+ for ($i = 0; $i < $numberOfGroups; ++$i) {
+ $size = $groupSize;
+ if ($i < $remain) {
+ ++$size;
+ }
+ if ($size) {
+ $groups->push(new static(array_slice($this->items, $start, $size)));
+ $start += $size;
+ }
+ }
+ return $groups;
+ }
+
+ /**
+ * Chunk the underlying collection array.
+ *
+ * @return static>
+ */
+ public function chunk(int $size): self
+ {
+ if ($size <= 0) {
+ return new static();
+ }
+ $chunks = [];
+ foreach (array_chunk($this->items, $size, true) as $chunk) {
+ $chunks[] = new static($chunk);
+ }
+ return new static($chunks);
+ }
+
+ /**
+ * Sort through each item with a callback.
+ *
+ * @param callable(TValue, TValue): int $callback
+ * @return static
+ */
+ public function sort(callable $callback = null): self
+ {
+ $items = $this->items;
+ $callback ? uasort($items, $callback) : asort($items);
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection using the given callback.
+ *
+ * @param (callable(TValue, TKey): mixed)|string|array $callback
+ * @return static
+ */
+ public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ if (is_array($callback) && ! is_callable($callback)) {
+ return $this->sortByMany($callback);
+ }
+
+ $results = [];
+ $callback = $this->valueRetriever($callback);
+ // First we will loop through the items and get the comparator from a callback
+ // function which we were given. Then, we will sort the returned values and
+ // and grab the corresponding values for the sorted keys from this array.
+ foreach ($this->items as $key => $value) {
+ $results[$key] = $callback($value, $key);
+ }
+ $descending ? arsort($results, $options) : asort($results, $options);
+ // Once we have sorted all of the keys in the array, we will loop through them
+ // and grab the corresponding model so we can set the underlying items list
+ // to the sorted version. Then we'll just return the collection instance.
+ foreach (array_keys($results) as $key) {
+ $results[$key] = $this->items[$key];
+ }
+ return new static($results);
+ }
+
+ /**
+ * Sort the collection in descending order using the given callback.
+ *
+ * @param (callable(TValue, TKey): mixed)|string $callback
+ * @return static
+ */
+ public function sortByDesc($callback, int $options = SORT_REGULAR): self
+ {
+ return $this->sortBy($callback, $options, true);
+ }
+
+ /**
+ * Sort the collection keys.
+ *
+ * @return static
+ */
+ public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ $items = $this->items;
+ $descending ? krsort($items, $options) : ksort($items, $options);
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection keys in descending order.
+ *
+ * @return static
+ */
+ public function sortKeysDesc(int $options = SORT_REGULAR): self
+ {
+ return $this->sortKeys($options, true);
+ }
+
+ /**
+ * Splice a portion of the underlying collection array.
+ *
+ * @param array $replacement
+ * @return static
+ */
+ public function splice(int $offset, int $length = null, $replacement = []): self
+ {
+ if (func_num_args() === 1) {
+ return new static(array_splice($this->items, $offset));
+ }
+ return new static(array_splice($this->items, $offset, $length, $replacement));
+ }
+
+ /**
+ * Get the sum of the given values.
+ *
+ * @param (callable(TValue): mixed)|string|null $callback
+ * @return mixed
+ */
+ public function sum($callback = null)
+ {
+ if (is_null($callback)) {
+ return array_sum($this->items);
+ }
+ $callback = $this->valueRetriever($callback);
+ return $this->reduce(function ($result, $item) use ($callback) {
+ return $result + $callback($item);
+ }, 0);
+ }
+
+ /**
+ * Take the first or last {$limit} items.
+ *
+ * @return static
+ */
+ public function take(int $limit): self
+ {
+ if ($limit < 0) {
+ return $this->slice($limit, abs($limit));
+ }
+ return $this->slice(0, $limit);
+ }
+
+ /**
+ * Pass the collection to the given callback and then return it.
+ *
+ * @param callable(static): mixed $callback
+ * @return $this
+ */
+ public function tap(callable $callback): self
+ {
+ $callback(new static($this->items));
+ return $this;
+ }
+
+ /**
+ * Transform each item in the collection using a callback.
+ *
+ * @param callable(TValue, TKey): TValue $callback
+ * @return $this
+ */
+ public function transform(callable $callback): self
+ {
+ $this->items = $this->map($callback)->all();
+ return $this;
+ }
+
+ /**
+ * Return only unique items from the collection array.
+ *
+ * @param (callable(TValue, TKey): bool)|string|null $key
+ * @return static
+ */
+ public function unique($key = null, bool $strict = false): self
+ {
+ $callback = $this->valueRetriever($key);
+ $exists = [];
+ return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+ if (in_array($id = $callback($item, $key), $exists, $strict)) {
+ return true;
+ }
+ $exists[] = $id;
+ });
+ }
+
+ /**
+ * Return only unique items from the collection array using strict comparison.
+ *
+ * @param (callable(TValue, TKey): bool)|string|null $key
+ * @return static
+ */
+ public function uniqueStrict($key = null): self
+ {
+ return $this->unique($key, true);
+ }
+
+ /**
+ * Reset the keys on the underlying array.
+ *
+ * @return static
+ */
+ public function values(): self
+ {
+ return new static(array_values($this->items));
+ }
+
+ /**
+ * Zip the collection together with one or more arrays.
+ * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+ * => [[1, 4], [2, 5], [3, 6]].
+ *
+ * @template TZipValue
+ *
+ * @param Arrayable|iterable ...$items
+ * @return static>
+ */
+ public function zip($items): self
+ {
+ $arrayableItems = array_map(function ($items) {
+ return $this->getArrayableItems($items);
+ }, func_get_args());
+ $params = array_merge([
+ function () {
+ return new static(func_get_args());
+ },
+ $this->items,
+ ], $arrayableItems);
+ return new static(call_user_func_array('array_map', $params));
+ }
+
+ /**
+ * Pad collection to the specified length with a value.
+ *
+ * @template TPadValue
+ *
+ * @param TPadValue $value
+ * @return static
+ */
+ public function pad(int $size, $value): self
+ {
+ return new static(array_pad($this->items, $size, $value));
+ }
+
+ /**
+ * Get the collection of items as a plain array.
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return array_map(function ($value) {
+ return $value instanceof Arrayable ? $value->toArray() : $value;
+ }, $this->items);
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ public function jsonSerialize(): array
+ {
+ return array_map(function ($value) {
+ if ($value instanceof JsonSerializable) {
+ return $value->jsonSerialize();
+ }
+ if ($value instanceof Jsonable) {
+ return json_decode($value->__toString(), true);
+ }
+ if ($value instanceof Arrayable) {
+ return $value->toArray();
+ }
+ return $value;
+ }, $this->items);
+ }
+
+ /**
+ * Get the collection of items as JSON.
+ */
+ public function toJson(int $options = 0): string
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Get an iterator for the items.
+ *
+ * @return ArrayIterator
+ */
+ public function getIterator(): ArrayIterator
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ /**
+ * Get a CachingIterator instance.
+ */
+ public function getCachingIterator(int $flags = CachingIterator::CALL_TOSTRING): CachingIterator
+ {
+ return new CachingIterator($this->getIterator(), $flags);
+ }
+
+ /**
+ * Count the number of items in the collection.
+ */
+ public function count(): int
+ {
+ return count($this->items);
+ }
+
+ /**
+ * Get a base Support collection instance from this collection.
+ *
+ * @return Collection
+ */
+ public function toBase()
+ {
+ return new self($this);
+ }
+
+ /**
+ * Determine if an item exists at an offset.
+ *
+ * @param TKey $key
+ * @return bool
+ */
+ public function offsetExists($key)
+ {
+ return array_key_exists($key, $this->items);
+ }
+
+ /**
+ * Get an item at a given offset.
+ *
+ * @param TKey $key
+ * @return TValue
+ */
+ public function offsetGet($key)
+ {
+ return $this->items[$key];
+ }
+
+ /**
+ * Set the item at a given offset.
+ *
+ * @param null|TKey $key
+ * @param TValue $value
+ */
+ public function offsetSet($key, $value)
+ {
+ if (is_null($key)) {
+ $this->items[] = $value;
+ } else {
+ $this->items[$key] = $value;
+ }
+ }
+
+ /**
+ * Unset the item at a given offset.
+ *
+ * @param TKey $key
+ */
+ public function offsetUnset($key)
+ {
+ unset($this->items[$key]);
+ }
+
+ /**
+ * Add a method to the list of proxied methods.
+ */
+ public static function proxy(string $method): void
+ {
+ static::$proxies[] = $method;
+ }
+
+ /**
+ * Sort the collection using multiple comparisons.
+ *
+ * @return static
+ */
+ protected function sortByMany(array $comparisons = [])
+ {
+ $items = $this->items;
+
+ usort($items, function ($a, $b) use ($comparisons) {
+ foreach ($comparisons as $comparison) {
+ $comparison = Arr::wrap($comparison);
+
+ $prop = $comparison[0];
+
+ $ascending = Arr::get($comparison, 1, true) === true
+ || Arr::get($comparison, 1, true) === 'asc';
+
+ $result = 0;
+
+ if (! is_string($prop) && is_callable($prop)) {
+ $result = $prop($a, $b);
+ } else {
+ $values = [data_get($a, $prop), data_get($b, $prop)];
+
+ if (! $ascending) {
+ $values = array_reverse($values);
+ }
+
+ $result = $values[0] <=> $values[1];
+ }
+
+ if ($result === 0) {
+ continue;
+ }
+
+ return $result;
+ }
+ });
+
+ return new static($items);
+ }
+
+ /**
+ * Get an operator checker callback.
+ * @param mixed|string $operator
+ * @param null|TValue $value
+ */
+ protected function operatorForWhere(string $key, $operator = null, $value = null): \Closure
+ {
+ if (func_num_args() === 1) {
+ $value = true;
+ $operator = '=';
+ }
+ if (func_num_args() === 2) {
+ $value = $operator;
+ $operator = '=';
+ }
+ return function ($item) use ($key, $operator, $value) {
+ $retrieved = data_get($item, $key);
+ $strings = array_filter([$retrieved, $value], function ($value) {
+ return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
+ });
+ if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
+ return in_array($operator, ['!=', '<>', '!==']);
+ }
+ switch ($operator) {
+ default:
+ case '=':
+ case '==':
+ return $retrieved == $value;
+ case '!=':
+ case '<>':
+ return $retrieved != $value;
+ case '<':
+ return $retrieved < $value;
+ case '>':
+ return $retrieved > $value;
+ case '<=':
+ return $retrieved <= $value;
+ case '>=':
+ return $retrieved >= $value;
+ case '===':
+ return $retrieved === $value;
+ case '!==':
+ return $retrieved !== $value;
+ }
+ };
+ }
+
+ /**
+ * Determine if the given value is callable, but not a string.
+ * @param mixed $value
+ */
+ protected function useAsCallable($value): bool
+ {
+ return ! is_string($value) && is_callable($value);
+ }
+
+ /**
+ * Get a value retrieving callback.
+ * @param mixed $value
+ */
+ protected function valueRetriever($value): callable
+ {
+ if ($this->useAsCallable($value)) {
+ return $value;
+ }
+ return function ($item) use ($value) {
+ return data_get($item, $value);
+ };
+ }
+
+ /**
+ * Results array of items from Collection or Arrayable.
+ * @param null|Arrayable|iterable|Jsonable|JsonSerializable|static $items
+ * @return array
+ */
+ protected function getArrayableItems($items): array
+ {
+ if (is_array($items)) {
+ return $items;
+ }
+ if ($items instanceof self) {
+ return $items->all();
+ }
+ if ($items instanceof Arrayable) {
+ return $items->toArray();
+ }
+ if ($items instanceof Jsonable) {
+ return json_decode($items->__toString(), true);
+ }
+ if ($items instanceof JsonSerializable) {
+ return $items->jsonSerialize();
+ }
+ if ($items instanceof Traversable) {
+ return iterator_to_array($items);
+ }
+ return (array) $items;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Composer.php b/server/addons/epay/library/hyperf/utils/src/Composer.php
new file mode 100644
index 0000000..8d6f0c4
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Composer.php
@@ -0,0 +1,159 @@
+offsetGet('packages') ?? [];
+ $packagesDev = self::$content->offsetGet('packages-dev') ?? [];
+ foreach (array_merge($packages, $packagesDev) as $package) {
+ $packageName = '';
+ foreach ($package ?? [] as $key => $value) {
+ if ($key === 'name') {
+ $packageName = $value;
+ continue;
+ }
+ switch ($key) {
+ case 'extra':
+ $packageName && self::$extra[$packageName] = $value;
+ break;
+ case 'scripts':
+ $packageName && self::$scripts[$packageName] = $value;
+ break;
+ case 'version':
+ $packageName && self::$versions[$packageName] = $value;
+ break;
+ }
+ }
+ }
+ }
+ return self::$content;
+ }
+
+ public static function getJsonContent(): Collection
+ {
+ if (! self::$json) {
+ $path = BASE_PATH . '/composer.json';
+ if (! is_readable($path)) {
+ throw new \RuntimeException('composer.json is not readable.');
+ }
+ self::$json = collect(json_decode(file_get_contents($path), true));
+ }
+ return self::$json;
+ }
+
+ public static function discoverLockFile(): string
+ {
+ $path = '';
+ if (is_readable(BASE_PATH . '/composer.lock')) {
+ $path = BASE_PATH . '/composer.lock';
+ }
+ return $path;
+ }
+
+ public static function getMergedExtra(string $key = null)
+ {
+ if (! self::$extra) {
+ self::getLockContent();
+ }
+ if ($key === null) {
+ return self::$extra;
+ }
+ $extra = [];
+ foreach (self::$extra ?? [] as $project => $config) {
+ foreach ($config ?? [] as $configKey => $item) {
+ if ($key === $configKey && $item) {
+ foreach ($item ?? [] as $k => $v) {
+ if (is_array($v)) {
+ $extra[$k] = array_merge($extra[$k] ?? [], $v);
+ } else {
+ $extra[$k][] = $v;
+ }
+ }
+ }
+ }
+ }
+ return $extra;
+ }
+
+ public static function getLoader(): ClassLoader
+ {
+ if (! self::$classLoader) {
+ self::$classLoader = self::findLoader();
+ }
+ return self::$classLoader;
+ }
+
+ public static function setLoader(ClassLoader $classLoader): ClassLoader
+ {
+ self::$classLoader = $classLoader;
+ return $classLoader;
+ }
+
+ private static function findLoader(): ClassLoader
+ {
+ $composerClass = '';
+ foreach (get_declared_classes() as $declaredClass) {
+ if (strpos($declaredClass, 'ComposerAutoloaderInit') === 0 && method_exists($declaredClass, 'getLoader')) {
+ $composerClass = $declaredClass;
+ break;
+ }
+ }
+ if (! $composerClass) {
+ throw new \RuntimeException('Composer loader not found.');
+ }
+ return $composerClass::getLoader();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/ConfigProvider.php b/server/addons/epay/library/hyperf/utils/src/ConfigProvider.php
new file mode 100644
index 0000000..c2e9abf
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/ConfigProvider.php
@@ -0,0 +1,37 @@
+ [
+ Serializer::class => SerializerFactory::class,
+ NormalizerInterface::class => SimpleNormalizer::class,
+ ],
+ 'annotations' => [
+ 'scan' => [
+ 'paths' => [
+ __DIR__,
+ ],
+ ],
+ ],
+ ];
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Context.php b/server/addons/epay/library/hyperf/utils/src/Context.php
new file mode 100644
index 0000000..0db022e
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Context.php
@@ -0,0 +1,107 @@
+exchangeArray($keys ? Arr::only($from->getArrayCopy(), $keys) : $from->getArrayCopy());
+ }
+
+ /**
+ * Retrieve the value and override it by closure.
+ */
+ public static function override(string $id, \Closure $closure)
+ {
+ $value = null;
+ if (self::has($id)) {
+ $value = self::get($id);
+ }
+ $value = $closure($value);
+ self::set($id, $value);
+ return $value;
+ }
+
+ /**
+ * Retrieve the value and store it if not exists.
+ * @param mixed $value
+ */
+ public static function getOrSet(string $id, $value)
+ {
+ if (! self::has($id)) {
+ return self::set($id, value($value));
+ }
+ return self::get($id);
+ }
+
+ public static function getContainer()
+ {
+ if (Coroutine::inCoroutine()) {
+ return Co::getContextFor();
+ }
+
+ return static::$nonCoContext;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Contracts/Arrayable.php b/server/addons/epay/library/hyperf/utils/src/Contracts/Arrayable.php
new file mode 100644
index 0000000..d588c44
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Contracts/Arrayable.php
@@ -0,0 +1,24 @@
+
+ */
+ public function toArray(): array;
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Contracts/Jsonable.php b/server/addons/epay/library/hyperf/utils/src/Contracts/Jsonable.php
new file mode 100644
index 0000000..01d3668
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Contracts/Jsonable.php
@@ -0,0 +1,17 @@
+channel = new Channel(1);
+ }
+
+ /**
+ * Yield the current coroutine for a given timeout,
+ * unless the coordinator is woke up from outside.
+ *
+ * @param float|int $timeout
+ * @return bool returns true if the coordinator has been woken up
+ */
+ public function yield($timeout = -1): bool
+ {
+ $this->channel->pop((float) $timeout);
+ return $this->channel->isClosing();
+ }
+
+ /**
+ * Wakeup all coroutines yielding for this coordinator.
+ */
+ public function resume(): void
+ {
+ $this->channel->close();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Coordinator/CoordinatorManager.php b/server/addons/epay/library/hyperf/utils/src/Coordinator/CoordinatorManager.php
new file mode 100644
index 0000000..e82e433
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Coordinator/CoordinatorManager.php
@@ -0,0 +1,53 @@
+has(StdoutLoggerInterface::class)) {
+ /* @var LoggerInterface $logger */
+ $logger = $container->get(StdoutLoggerInterface::class);
+ /* @var FormatterInterface $formatter */
+ if ($container->has(FormatterInterface::class)) {
+ $formatter = $container->get(FormatterInterface::class);
+ $logger->warning($formatter->format($throwable));
+ } else {
+ $logger->warning(sprintf('Uncaptured exception[%s] detected in %s::%d.', get_class($throwable), $throwable->getFile(), $throwable->getLine()));
+ }
+ }
+ }
+ }
+ });
+
+ try {
+ return $coroutine->getId();
+ } catch (\Throwable $exception) {
+ return -1;
+ }
+ }
+
+ public static function inCoroutine(): bool
+ {
+ return Co::id() > 0;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Coroutine/Concurrent.php b/server/addons/epay/library/hyperf/utils/src/Coroutine/Concurrent.php
new file mode 100644
index 0000000..c029c6a
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Coroutine/Concurrent.php
@@ -0,0 +1,95 @@
+limit = $limit;
+ $this->channel = new Channel($limit);
+ }
+
+ public function __call($name, $arguments)
+ {
+ if (in_array($name, ['isFull', 'isEmpty'])) {
+ return $this->channel->{$name}(...$arguments);
+ }
+ }
+
+ public function getLimit(): int
+ {
+ return $this->limit;
+ }
+
+ public function length(): int
+ {
+ return $this->channel->getLength();
+ }
+
+ public function getLength(): int
+ {
+ return $this->channel->getLength();
+ }
+
+ public function getRunningCoroutineCount(): int
+ {
+ return $this->getLength();
+ }
+
+ public function getChannel(): Channel
+ {
+ return $this->channel;
+ }
+
+ public function create(callable $callable): void
+ {
+ $this->channel->push(true);
+
+ Coroutine::create(function () use ($callable) {
+ try {
+ $callable();
+ } catch (\Throwable $exception) {
+ if (ApplicationContext::hasContainer()) {
+ $container = ApplicationContext::getContainer();
+ if ($container->has(StdoutLoggerInterface::class) && $container->has(FormatterInterface::class)) {
+ $logger = $container->get(StdoutLoggerInterface::class);
+ $formatter = $container->get(FormatterInterface::class);
+ $logger->error($formatter->format($exception));
+ }
+ }
+ } finally {
+ $this->channel->pop();
+ }
+ });
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Coroutine/Locker.php b/server/addons/epay/library/hyperf/utils/src/Coroutine/Locker.php
new file mode 100644
index 0000000..31c155c
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Coroutine/Locker.php
@@ -0,0 +1,60 @@
+ 0) {
+ SwooleCoroutine::resume($id);
+ }
+ }
+ self::clear($key);
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Exception/ChannelClosedException.php b/server/addons/epay/library/hyperf/utils/src/Exception/ChannelClosedException.php
new file mode 100644
index 0000000..52c7a42
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Exception/ChannelClosedException.php
@@ -0,0 +1,16 @@
+throwable = $throwable;
+ }
+
+ public function getThrowable(): Throwable
+ {
+ return $this->throwable;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Exception/InvalidArgumentException.php b/server/addons/epay/library/hyperf/utils/src/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000..f4155ec
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Exception/InvalidArgumentException.php
@@ -0,0 +1,16 @@
+results;
+ }
+
+ public function setResults(array $results)
+ {
+ $this->results = $results;
+ }
+
+ public function getThrowables()
+ {
+ return $this->throwables;
+ }
+
+ public function setThrowables(array $throwables)
+ {
+ return $this->throwables = $throwables;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Exception/TimeoutException.php b/server/addons/epay/library/hyperf/utils/src/Exception/TimeoutException.php
new file mode 100644
index 0000000..7ff9e02
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Exception/TimeoutException.php
@@ -0,0 +1,16 @@
+isFile($path)) {
+ return $lock ? $this->sharedGet($path) : file_get_contents($path);
+ }
+
+ throw new FileNotFoundException("File does not exist at path {$path}");
+ }
+
+ /**
+ * Get contents of a file with shared access.
+ */
+ public function sharedGet(string $path): string
+ {
+ return $this->atomic($path, function ($path) {
+ $contents = '';
+ $handle = fopen($path, 'rb');
+ if ($handle) {
+ $wouldBlock = false;
+ flock($handle, LOCK_SH | LOCK_NB, $wouldBlock);
+ while ($wouldBlock) {
+ usleep(1000);
+ flock($handle, LOCK_SH | LOCK_NB, $wouldBlock);
+ }
+ try {
+ clearstatcache(true, $path);
+ $contents = fread($handle, $this->size($path) ?: 1);
+ } finally {
+ flock($handle, LOCK_UN);
+ fclose($handle);
+ }
+ }
+ return $contents;
+ });
+ }
+
+ /**
+ * Get the returned value of a file.
+ *
+ * @throws \Hyperf\Utils\Filesystem\FileNotFoundException
+ */
+ public function getRequire(string $path)
+ {
+ if ($this->isFile($path)) {
+ return require $path;
+ }
+
+ throw new FileNotFoundException("File does not exist at path {$path}");
+ }
+
+ /**
+ * Require the given file once.
+ *
+ * @return mixed
+ */
+ public function requireOnce(string $file)
+ {
+ require_once $file;
+ }
+
+ /**
+ * Get the MD5 hash of the file at the given path.
+ */
+ public function hash(string $path): string
+ {
+ return md5_file($path);
+ }
+
+ /**
+ * Clears file status cache.
+ */
+ public function clearStatCache(string $path): void
+ {
+ clearstatcache(true, $path);
+ }
+
+ /**
+ * Write the contents of a file.
+ *
+ * @param resource|string $contents
+ * @return bool|int
+ */
+ public function put(string $path, $contents, bool $lock = false)
+ {
+ if ($lock) {
+ return $this->atomic($path, function ($path) use ($contents) {
+ $handle = fopen($path, 'w+');
+ if ($handle) {
+ $wouldBlock = false;
+ flock($handle, LOCK_EX | LOCK_NB, $wouldBlock);
+ while ($wouldBlock) {
+ usleep(1000);
+ flock($handle, LOCK_EX | LOCK_NB, $wouldBlock);
+ }
+ try {
+ fwrite($handle, $contents);
+ } finally {
+ flock($handle, LOCK_UN);
+ fclose($handle);
+ }
+ }
+ });
+ }
+ return file_put_contents($path, $contents);
+ }
+
+ /**
+ * Write the contents of a file, replacing it atomically if it already exists.
+ */
+ public function replace(string $path, string $content)
+ {
+ // If the path already exists and is a symlink, get the real path...
+ clearstatcache(true, $path);
+
+ $path = realpath($path) ?: $path;
+
+ $tempPath = tempnam(dirname($path), basename($path));
+
+ // Fix permissions of tempPath because `tempnam()` creates it with permissions set to 0600...
+ chmod($tempPath, 0777 - umask());
+
+ file_put_contents($tempPath, $content);
+
+ rename($tempPath, $path);
+ }
+
+ /**
+ * Prepend to a file.
+ */
+ public function prepend(string $path, string $data): int
+ {
+ if ($this->exists($path)) {
+ return $this->put($path, $data . $this->get($path));
+ }
+
+ return $this->put($path, $data);
+ }
+
+ /**
+ * Append to a file.
+ */
+ public function append(string $path, string $data): int
+ {
+ return file_put_contents($path, $data, FILE_APPEND);
+ }
+
+ /**
+ * Get or set UNIX mode of a file or directory.
+ */
+ public function chmod(string $path, ?int $mode = null)
+ {
+ if ($mode) {
+ return chmod($path, $mode);
+ }
+
+ return substr(sprintf('%o', fileperms($path)), -4);
+ }
+
+ /**
+ * Delete the file at a given path.
+ *
+ * @param array|string $paths
+ */
+ public function delete($paths): bool
+ {
+ $paths = is_array($paths) ? $paths : func_get_args();
+
+ $success = true;
+
+ foreach ($paths as $path) {
+ try {
+ if (! @unlink($path)) {
+ $success = false;
+ }
+ } catch (ErrorException $e) {
+ $success = false;
+ }
+ }
+
+ return $success;
+ }
+
+ /**
+ * Move a file to a new location.
+ */
+ public function move(string $path, string $target): bool
+ {
+ return rename($path, $target);
+ }
+
+ /**
+ * Copy a file to a new location.
+ */
+ public function copy(string $path, string $target): bool
+ {
+ return copy($path, $target);
+ }
+
+ /**
+ * Create a hard link to the target file or directory.
+ */
+ public function link(string $target, string $link)
+ {
+ if (! $this->windowsOs()) {
+ return symlink($target, $link);
+ }
+
+ $mode = $this->isDirectory($target) ? 'J' : 'H';
+
+ exec("mklink /{$mode} \"{$link}\" \"{$target}\"");
+ }
+
+ /**
+ * Extract the file name from a file path.
+ */
+ public function name(string $path): string
+ {
+ return pathinfo($path, PATHINFO_FILENAME);
+ }
+
+ /**
+ * Extract the trailing name component from a file path.
+ */
+ public function basename(string $path): string
+ {
+ return pathinfo($path, PATHINFO_BASENAME);
+ }
+
+ /**
+ * Extract the parent directory from a file path.
+ */
+ public function dirname(string $path): string
+ {
+ return pathinfo($path, PATHINFO_DIRNAME);
+ }
+
+ /**
+ * Extract the file extension from a file path.
+ */
+ public function extension(string $path): string
+ {
+ return pathinfo($path, PATHINFO_EXTENSION);
+ }
+
+ /**
+ * Get the file type of a given file.
+ */
+ public function type(string $path): string
+ {
+ return filetype($path);
+ }
+
+ /**
+ * Get the mime-type of a given file.
+ *
+ * @return false|string
+ */
+ public function mimeType(string $path)
+ {
+ return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
+ }
+
+ /**
+ * Get the file size of a given file.
+ */
+ public function size(string $path): int
+ {
+ return filesize($path);
+ }
+
+ /**
+ * Get the file's last modification time.
+ */
+ public function lastModified(string $path): int
+ {
+ return filemtime($path);
+ }
+
+ /**
+ * Determine if the given path is a directory.
+ */
+ public function isDirectory(string $directory): bool
+ {
+ return is_dir($directory);
+ }
+
+ /**
+ * Determine if the given path is readable.
+ */
+ public function isReadable(string $path): bool
+ {
+ return is_readable($path);
+ }
+
+ /**
+ * Determine if the given path is writable.
+ */
+ public function isWritable(string $path): bool
+ {
+ return is_writable($path);
+ }
+
+ /**
+ * Determine if the given path is a file.
+ */
+ public function isFile(string $file): bool
+ {
+ return is_file($file);
+ }
+
+ /**
+ * Find path names matching a given pattern.
+ */
+ public function glob(string $pattern, int $flags = 0): array
+ {
+ return glob($pattern, $flags);
+ }
+
+ /**
+ * Get an array of all files in a directory.
+ *
+ * @return \Symfony\Component\Finder\SplFileInfo[]
+ */
+ public function files(string $directory, bool $hidden = false): array
+ {
+ return iterator_to_array(
+ Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->depth(0)->sortByName(),
+ false
+ );
+ }
+
+ /**
+ * Get all of the files from the given directory (recursive).
+ * @return \Symfony\Component\Finder\SplFileInfo[]
+ */
+ public function allFiles(string $directory, bool $hidden = false): array
+ {
+ return iterator_to_array(
+ Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->sortByName(),
+ false
+ );
+ }
+
+ /**
+ * Get all of the directories within a given directory.
+ */
+ public function directories(string $directory): array
+ {
+ $directories = [];
+
+ foreach (Finder::create()->in($directory)->directories()->depth(0)->sortByName() as $dir) {
+ $directories[] = $dir->getPathname();
+ }
+
+ return $directories;
+ }
+
+ /**
+ * Create a directory.
+ */
+ public function makeDirectory(string $path, int $mode = 0755, bool $recursive = false, bool $force = false): bool
+ {
+ if ($force) {
+ return @mkdir($path, $mode, $recursive);
+ }
+
+ return mkdir($path, $mode, $recursive);
+ }
+
+ /**
+ * Move a directory.
+ */
+ public function moveDirectory(string $from, string $to, bool $overwrite = false): bool
+ {
+ if ($overwrite && $this->isDirectory($to) && ! $this->deleteDirectory($to)) {
+ return false;
+ }
+
+ return @rename($from, $to) === true;
+ }
+
+ /**
+ * Copy a directory from one location to another.
+ */
+ public function copyDirectory(string $directory, string $destination, int $options = null): bool
+ {
+ if (! $this->isDirectory($directory)) {
+ return false;
+ }
+
+ $options = $options ?: FilesystemIterator::SKIP_DOTS;
+
+ // If the destination directory does not actually exist, we will go ahead and
+ // create it recursively, which just gets the destination prepared to copy
+ // the files over. Once we make the directory we'll proceed the copying.
+ if (! $this->isDirectory($destination)) {
+ $this->makeDirectory($destination, 0777, true);
+ }
+
+ $items = new FilesystemIterator($directory, $options);
+
+ foreach ($items as $item) {
+ // As we spin through items, we will check to see if the current file is actually
+ // a directory or a file. When it is actually a directory we will need to call
+ // back into this function recursively to keep copying these nested folders.
+ $target = $destination . DIRECTORY_SEPARATOR . $item->getBasename();
+
+ if ($item->isDir()) {
+ $path = $item->getPathname();
+
+ if (! $this->copyDirectory($path, $target, $options)) {
+ return false;
+ }
+ }
+
+ // If the current items is just a regular file, we will just copy this to the new
+ // location and keep looping. If for some reason the copy fails we'll bail out
+ // and return false, so the developer is aware that the copy process failed.
+ else {
+ if (! $this->copy($item->getPathname(), $target)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Recursively delete a directory.
+ *
+ * The directory itself may be optionally preserved.
+ */
+ public function deleteDirectory(string $directory, bool $preserve = false): bool
+ {
+ if (! $this->isDirectory($directory)) {
+ return false;
+ }
+
+ $items = new FilesystemIterator($directory);
+
+ foreach ($items as $item) {
+ // If the item is a directory, we can just recurse into the function and
+ // delete that sub-directory otherwise we'll just delete the file and
+ // keep iterating through each file until the directory is cleaned.
+ if ($item->isDir() && ! $item->isLink()) {
+ $this->deleteDirectory($item->getPathname());
+ }
+
+ // If the item is just a file, we can go ahead and delete it since we're
+ // just looping through and waxing all of the files in this directory
+ // and calling directories recursively, so we delete the real path.
+ else {
+ $this->delete($item->getPathname());
+ }
+ }
+
+ if (! $preserve) {
+ @rmdir($directory);
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove all of the directories within a given directory.
+ */
+ public function deleteDirectories(string $directory): bool
+ {
+ $allDirectories = $this->directories($directory);
+
+ if (! empty($allDirectories)) {
+ foreach ($allDirectories as $directoryName) {
+ $this->deleteDirectory($directoryName);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Empty the specified directory of all files and folders.
+ */
+ public function cleanDirectory(string $directory): bool
+ {
+ return $this->deleteDirectory($directory, true);
+ }
+
+ /**
+ * Detect whether it's Windows.
+ */
+ public function windowsOs(): bool
+ {
+ return stripos(PHP_OS, 'win') === 0;
+ }
+
+ protected function atomic($path, $callback)
+ {
+ if (Coroutine::inCoroutine()) {
+ try {
+ while (! Coroutine\Locker::lock($path)) {
+ usleep(1000);
+ }
+ return $callback($path);
+ } finally {
+ Coroutine\Locker::unlock($path);
+ }
+ } else {
+ return $callback($path);
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Fluent.php b/server/addons/epay/library/hyperf/utils/src/Fluent.php
new file mode 100644
index 0000000..0f1cf84
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Fluent.php
@@ -0,0 +1,200 @@
+ $value) {
+ $this->attributes[$key] = $value;
+ }
+ }
+
+ /**
+ * Handle dynamic calls to the fluent instance to set attributes.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return $this
+ */
+ public function __call($method, $parameters)
+ {
+ $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true;
+
+ return $this;
+ }
+
+ /**
+ * Dynamically retrieve the value of an attribute.
+ *
+ * @param string $key
+ */
+ public function __get($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Dynamically set the value of an attribute.
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function __set($key, $value)
+ {
+ $this->offsetSet($key, $value);
+ }
+
+ /**
+ * Dynamically check if an attribute is set.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function __isset($key)
+ {
+ return $this->offsetExists($key);
+ }
+
+ /**
+ * Dynamically unset an attribute.
+ *
+ * @param string $key
+ */
+ public function __unset($key)
+ {
+ $this->offsetUnset($key);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * Get an attribute from the fluent instance.
+ *
+ * @param string $key
+ * @param null|mixed $default
+ */
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->attributes)) {
+ return $this->attributes[$key];
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Get the attributes from the fluent instance.
+ *
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Convert the fluent instance to an array.
+ */
+ public function toArray(): array
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the fluent instance to JSON.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Determine if the given offset exists.
+ *
+ * @param string $offset
+ * @return bool
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->attributes[$offset]);
+ }
+
+ /**
+ * Get the value for a given offset.
+ *
+ * @param string $offset
+ */
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ /**
+ * Set the value at the given offset.
+ *
+ * @param string $offset
+ * @param mixed $value
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->attributes[$offset] = $value;
+ }
+
+ /**
+ * Unset the value at the given offset.
+ *
+ * @param string $offset
+ */
+ public function offsetUnset($offset)
+ {
+ unset($this->attributes[$offset]);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Functions.php b/server/addons/epay/library/hyperf/utils/src/Functions.php
new file mode 100644
index 0000000..02b20f0
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Functions.php
@@ -0,0 +1,486 @@
+ 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') {
+ return substr($value, 1, -1);
+ }
+ return $value;
+ }
+}
+if (! function_exists('retry')) {
+ /**
+ * Retry an operation a given number of times.
+ *
+ * @param float|int $times
+ * @param int $sleep millisecond
+ * @throws \Throwable
+ */
+ function retry($times, callable $callback, int $sleep = 0)
+ {
+ $attempts = 0;
+ $backoff = new Backoff($sleep);
+
+ beginning:
+ try {
+ return $callback(++$attempts);
+ } catch (\Throwable $e) {
+ if (--$times < 0) {
+ throw $e;
+ }
+
+ $backoff->sleep();
+ goto beginning;
+ }
+ }
+}
+if (! function_exists('with')) {
+ /**
+ * Return the given value, optionally passed through the given callback.
+ *
+ * @param mixed $value
+ */
+ function with($value, callable $callback = null)
+ {
+ return is_null($callback) ? $value : $callback($value);
+ }
+}
+
+if (! function_exists('collect')) {
+ /**
+ * Create a collection from the given value.
+ *
+ * @param null|mixed $value
+ * @return Collection
+ */
+ function collect($value = null)
+ {
+ return new Collection($value);
+ }
+}
+if (! function_exists('data_fill')) {
+ /**
+ * Fill in data where it's missing.
+ *
+ * @param mixed $target
+ * @param array|string $key
+ * @param mixed $value
+ */
+ function data_fill(&$target, $key, $value)
+ {
+ return data_set($target, $key, $value, false);
+ }
+}
+if (! function_exists('data_get')) {
+ /**
+ * Get an item from an array or object using "dot" notation.
+ *
+ * @param null|array|int|string $key
+ * @param null|mixed $default
+ * @param mixed $target
+ */
+ function data_get($target, $key, $default = null)
+ {
+ if (is_null($key)) {
+ return $target;
+ }
+
+ $key = is_array($key) ? $key : explode('.', is_int($key) ? (string) $key : $key);
+ while (! is_null($segment = array_shift($key))) {
+ if ($segment === '*') {
+ if ($target instanceof Collection) {
+ $target = $target->all();
+ } elseif (! is_array($target)) {
+ return value($default);
+ }
+ $result = [];
+ foreach ($target as $item) {
+ $result[] = data_get($item, $key);
+ }
+ return in_array('*', $key) ? Arr::collapse($result) : $result;
+ }
+ if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+ $target = $target[$segment];
+ } elseif (is_object($target) && isset($target->{$segment})) {
+ $target = $target->{$segment};
+ } else {
+ return value($default);
+ }
+ }
+ return $target;
+ }
+}
+if (! function_exists('data_set')) {
+ /**
+ * Set an item on an array or object using dot notation.
+ *
+ * @param mixed $target
+ * @param array|string $key
+ * @param bool $overwrite
+ * @param mixed $value
+ */
+ function data_set(&$target, $key, $value, $overwrite = true)
+ {
+ $segments = is_array($key) ? $key : explode('.', $key);
+ if (($segment = array_shift($segments)) === '*') {
+ if (! Arr::accessible($target)) {
+ $target = [];
+ }
+ if ($segments) {
+ foreach ($target as &$inner) {
+ data_set($inner, $segments, $value, $overwrite);
+ }
+ } elseif ($overwrite) {
+ foreach ($target as &$inner) {
+ $inner = $value;
+ }
+ }
+ } elseif (Arr::accessible($target)) {
+ if ($segments) {
+ if (! Arr::exists($target, $segment)) {
+ $target[$segment] = [];
+ }
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ($overwrite || ! Arr::exists($target, $segment)) {
+ $target[$segment] = $value;
+ }
+ } elseif (is_object($target)) {
+ if ($segments) {
+ if (! isset($target->{$segment})) {
+ $target->{$segment} = [];
+ }
+ data_set($target->{$segment}, $segments, $value, $overwrite);
+ } elseif ($overwrite || ! isset($target->{$segment})) {
+ $target->{$segment} = $value;
+ }
+ } else {
+ $target = [];
+ if ($segments) {
+ $target[$segment] = [];
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ($overwrite) {
+ $target[$segment] = $value;
+ }
+ }
+ return $target;
+ }
+}
+if (! function_exists('head')) {
+ /**
+ * Get the first element of an array. Useful for method chaining.
+ *
+ * @param array $array
+ */
+ function head($array)
+ {
+ return reset($array);
+ }
+}
+if (! function_exists('last')) {
+ /**
+ * Get the last element from an array.
+ *
+ * @param array $array
+ */
+ function last($array)
+ {
+ return end($array);
+ }
+}
+if (! function_exists('tap')) {
+ /**
+ * Call the given Closure with the given value then return the value.
+ *
+ * @param null|callable $callback
+ * @param mixed $value
+ */
+ function tap($value, $callback = null)
+ {
+ if (is_null($callback)) {
+ return new HigherOrderTapProxy($value);
+ }
+ $callback($value);
+ return $value;
+ }
+}
+
+if (! function_exists('call')) {
+ /**
+ * Call a callback with the arguments.
+ *
+ * @param mixed $callback
+ * @return null|mixed
+ */
+ function call($callback, array $args = [])
+ {
+ $result = null;
+ if ($callback instanceof \Closure) {
+ $result = $callback(...$args);
+ } elseif (is_object($callback) || (is_string($callback) && function_exists($callback))) {
+ $result = $callback(...$args);
+ } elseif (is_array($callback)) {
+ [$object, $method] = $callback;
+ $result = is_object($object) ? $object->{$method}(...$args) : $object::$method(...$args);
+ } else {
+ $result = call_user_func_array($callback, $args);
+ }
+ return $result;
+ }
+}
+
+if (! function_exists('go')) {
+ /**
+ * @return bool|int
+ */
+ function go(callable $callable)
+ {
+ $id = Coroutine::create($callable);
+ return $id > 0 ? $id : false;
+ }
+}
+
+if (! function_exists('co')) {
+ /**
+ * @return bool|int
+ */
+ function co(callable $callable)
+ {
+ $id = Coroutine::create($callable);
+ return $id > 0 ? $id : false;
+ }
+}
+
+if (! function_exists('defer')) {
+ function defer(callable $callable): void
+ {
+ Coroutine::defer($callable);
+ }
+}
+
+if (! function_exists('class_basename')) {
+ /**
+ * Get the class "basename" of the given object / class.
+ *
+ * @param object|string $class
+ * @return string
+ */
+ function class_basename($class)
+ {
+ $class = is_object($class) ? get_class($class) : $class;
+
+ return basename(str_replace('\\', '/', $class));
+ }
+}
+
+if (! function_exists('trait_uses_recursive')) {
+ /**
+ * Returns all traits used by a trait and its traits.
+ *
+ * @param string $trait
+ * @return array
+ */
+ function trait_uses_recursive($trait)
+ {
+ $traits = class_uses($trait);
+
+ foreach ($traits as $trait) {
+ $traits += trait_uses_recursive($trait);
+ }
+
+ return $traits;
+ }
+}
+
+if (! function_exists('class_uses_recursive')) {
+ /**
+ * Returns all traits used by a class, its parent classes and trait of their traits.
+ *
+ * @param object|string $class
+ * @return array
+ */
+ function class_uses_recursive($class)
+ {
+ if (is_object($class)) {
+ $class = get_class($class);
+ }
+
+ $results = [];
+
+ /* @phpstan-ignore-next-line */
+ foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
+ $results += trait_uses_recursive($class);
+ }
+
+ return array_unique($results);
+ }
+}
+
+if (! function_exists('setter')) {
+ /**
+ * Create a setter string.
+ */
+ function setter(string $property): string
+ {
+ return 'set' . Str::studly($property);
+ }
+}
+
+if (! function_exists('getter')) {
+ /**
+ * Create a getter string.
+ */
+ function getter(string $property): string
+ {
+ return 'get' . Str::studly($property);
+ }
+}
+
+if (! function_exists('parallel')) {
+ /**
+ * @param callable[] $callables
+ * @param int $concurrent if $concurrent is equal to 0, that means unlimit
+ */
+ function parallel(array $callables, int $concurrent = 0)
+ {
+ $parallel = new Parallel($concurrent);
+ foreach ($callables as $key => $callable) {
+ $parallel->add($callable, $key);
+ }
+ return $parallel->wait();
+ }
+}
+
+if (! function_exists('make')) {
+ /**
+ * Create an object instance, if the DI container exist in ApplicationContext,
+ * then the object will be created by DI container via `make()` method, if not,
+ * the object will create by `new` keyword.
+ */
+ function make(string $name, array $parameters = [])
+ {
+ if (ApplicationContext::hasContainer()) {
+ $container = ApplicationContext::getContainer();
+ if (method_exists($container, 'make')) {
+ return $container->make($name, $parameters);
+ }
+ }
+ $parameters = array_values($parameters);
+ return new $name(...$parameters);
+ }
+}
+
+if (! function_exists('run')) {
+ /**
+ * Run callable in non-coroutine environment, all hook functions by Swoole only available in the callable.
+ *
+ * @param array|callable $callbacks
+ */
+ function run($callbacks, int $flags = SWOOLE_HOOK_ALL): bool
+ {
+ if (Coroutine::inCoroutine()) {
+ throw new RuntimeException('Function \'run\' only execute in non-coroutine environment.');
+ }
+
+ \Swoole\Runtime::enableCoroutine($flags);
+
+ $result = \Swoole\Coroutine\Run(...(array) $callbacks);
+
+ \Swoole\Runtime::enableCoroutine(false);
+ return $result;
+ }
+}
+
+if (! function_exists('swoole_hook_flags')) {
+ /**
+ * Return the default swoole hook flags, you can rewrite it by defining `SWOOLE_HOOK_FLAGS`.
+ */
+ function swoole_hook_flags(): int
+ {
+ return defined('SWOOLE_HOOK_FLAGS') ? SWOOLE_HOOK_FLAGS : SWOOLE_HOOK_ALL;
+ }
+}
+
+if (! function_exists('optional')) {
+ /**
+ * Provide access to optional objects.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ function optional($value = null, callable $callback = null)
+ {
+ if (is_null($callback)) {
+ return new Optional($value);
+ }
+ if (! is_null($value)) {
+ return $callback($value);
+ }
+ }
+}
+
+if (! function_exists('wait')) {
+ function wait(Closure $closure, ?float $timeout = null)
+ {
+ if (ApplicationContext::hasContainer()) {
+ $waiter = ApplicationContext::getContainer()->get(Waiter::class);
+ return $waiter->wait($closure, $timeout);
+ }
+ return (new Waiter())->wait($closure, $timeout);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/HigherOrderCollectionProxy.php b/server/addons/epay/library/hyperf/utils/src/HigherOrderCollectionProxy.php
new file mode 100644
index 0000000..21d9b04
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/HigherOrderCollectionProxy.php
@@ -0,0 +1,63 @@
+method = $method;
+ $this->collection = $collection;
+ }
+
+ /**
+ * Proxy accessing an attribute onto the collection items.
+ */
+ public function __get(string $key)
+ {
+ return $this->collection->{$this->method}(function ($value) use ($key) {
+ return is_array($value) ? $value[$key] : $value->{$key};
+ });
+ }
+
+ /**
+ * Proxy a method call onto the collection items.
+ */
+ public function __call(string $method, array $parameters)
+ {
+ return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
+ return $value->{$method}(...$parameters);
+ });
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/HigherOrderTapProxy.php b/server/addons/epay/library/hyperf/utils/src/HigherOrderTapProxy.php
new file mode 100644
index 0000000..ae07961
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/HigherOrderTapProxy.php
@@ -0,0 +1,46 @@
+target = $target;
+ }
+
+ /**
+ * Dynamically pass method calls to the target.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ $this->target->{$method}(...$parameters);
+
+ return $this->target;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/InteractsWithTime.php b/server/addons/epay/library/hyperf/utils/src/InteractsWithTime.php
new file mode 100644
index 0000000..09db79b
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/InteractsWithTime.php
@@ -0,0 +1,70 @@
+parseDateInterval($delay);
+
+ return $delay instanceof DateTimeInterface
+ ? max(0, $delay->getTimestamp() - $this->currentTime())
+ : (int) $delay;
+ }
+
+ /**
+ * Get the "available at" UNIX timestamp.
+ *
+ * @param \DateInterval|\DateTimeInterface|int $delay
+ */
+ protected function availableAt($delay = 0): int
+ {
+ $delay = $this->parseDateInterval($delay);
+
+ return $delay instanceof DateTimeInterface
+ ? $delay->getTimestamp()
+ : Carbon::now()->addSeconds($delay)->getTimestamp();
+ }
+
+ /**
+ * If the given value is an interval, convert it to a DateTime instance.
+ *
+ * @param \DateInterval|\DateTimeInterface|int $delay
+ * @return \DateTimeInterface|int
+ */
+ protected function parseDateInterval($delay)
+ {
+ if ($delay instanceof DateInterval) {
+ $delay = Carbon::now()->add($delay);
+ }
+
+ return $delay;
+ }
+
+ /**
+ * Get the current system time as a UNIX timestamp.
+ */
+ protected function currentTime(): int
+ {
+ return Carbon::now()->getTimestamp();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/MessageBag.php b/server/addons/epay/library/hyperf/utils/src/MessageBag.php
new file mode 100644
index 0000000..3b3e029
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/MessageBag.php
@@ -0,0 +1,351 @@
+ $value) {
+ $value = $value instanceof Arrayable ? $value->toArray() : (array) $value;
+
+ $this->messages[$key] = array_unique($value);
+ }
+ }
+
+ /**
+ * Convert the message bag to its string representation.
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * Get the keys present in the message bag.
+ */
+ public function keys(): array
+ {
+ return array_keys($this->messages);
+ }
+
+ /**
+ * Add a message to the message bag.
+ */
+ public function add(string $key, string $message): MessageBagContract
+ {
+ if ($this->isUnique($key, $message)) {
+ $this->messages[$key][] = $message;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Merge a new array of messages into the message bag.
+ *
+ * @param array|MessageProvider $messages
+ * @return $this
+ */
+ public function merge($messages)
+ {
+ if ($messages instanceof MessageProvider) {
+ $messages = $messages->getMessageBag()->getMessages();
+ }
+
+ $this->messages = array_merge_recursive($this->messages, $messages);
+
+ return $this;
+ }
+
+ /**
+ * Determine if messages exist for all of the given keys.
+ *
+ * @param null|array|string $key
+ */
+ public function has($key): bool
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ if (is_null($key)) {
+ return $this->any();
+ }
+
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $key) {
+ if ($this->first($key) === '') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if messages exist for any of the given keys.
+ *
+ * @param array|string $keys
+ */
+ public function hasAny($keys = []): bool
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ foreach ($keys as $key) {
+ if ($this->has($key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the first message from the message bag for a given key.
+ *
+ * @param string $key
+ * @param string $format
+ */
+ public function first($key = null, $format = null): string
+ {
+ $messages = is_null($key) ? $this->all($format) : $this->get($key, $format);
+
+ $firstMessage = Arr::first($messages, null, '');
+
+ return is_array($firstMessage) ? Arr::first($firstMessage) : $firstMessage;
+ }
+
+ /**
+ * Get all of the messages from the message bag for a given key.
+ */
+ public function get(string $key, ?string $format = null): array
+ {
+ // If the message exists in the message bag, we will transform it and return
+ // the message. Otherwise, we will check if the key is implicit & collect
+ // all the messages that match the given key and output it as an array.
+ if (array_key_exists($key, $this->messages)) {
+ return $this->transform(
+ $this->messages[$key],
+ $this->checkFormat($format),
+ $key
+ );
+ }
+
+ if (Str::contains($key, '*')) {
+ return $this->getMessagesForWildcardKey($key, $format);
+ }
+
+ return [];
+ }
+
+ /**
+ * Get all of the messages for every key in the message bag.
+ */
+ public function all(?string $format = null): array
+ {
+ $format = $this->checkFormat($format);
+
+ $all = [];
+
+ foreach ($this->messages as $key => $messages) {
+ $all = array_merge($all, $this->transform($messages, $format, $key));
+ }
+
+ return $all;
+ }
+
+ /**
+ * Get all of the unique messages for every key in the message bag.
+ */
+ public function unique(?string $format = null): array
+ {
+ return array_unique($this->all($format));
+ }
+
+ /**
+ * Get the raw messages in the message bag.
+ */
+ public function messages(): array
+ {
+ return $this->messages;
+ }
+
+ /**
+ * Get the raw messages in the message bag.
+ */
+ public function getMessages(): array
+ {
+ return $this->messages();
+ }
+
+ /**
+ * Get the messages for the instance.
+ */
+ public function getMessageBag(): MessageBagContract
+ {
+ return $this;
+ }
+
+ /**
+ * Get the default message format.
+ */
+ public function getFormat(): string
+ {
+ return $this->format;
+ }
+
+ /**
+ * Set the default message format.
+ */
+ public function setFormat(string $format = ':message'): self
+ {
+ $this->format = $format;
+
+ return $this;
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ */
+ public function isEmpty(): bool
+ {
+ return ! $this->any();
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ */
+ public function isNotEmpty(): bool
+ {
+ return $this->any();
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ */
+ public function any(): bool
+ {
+ return $this->count() > 0;
+ }
+
+ /**
+ * Get the number of messages in the message bag.
+ */
+ public function count(): int
+ {
+ return count($this->messages, COUNT_RECURSIVE) - count($this->messages);
+ }
+
+ /**
+ * Get the instance as an array.
+ */
+ public function toArray(): array
+ {
+ return $this->getMessages();
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ */
+ public function jsonSerialize(): array
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ */
+ public function toJson(int $options = 0): string
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Determine if a key and message combination already exists.
+ */
+ protected function isUnique(string $key, string $message): bool
+ {
+ $messages = (array) $this->messages;
+
+ return ! isset($messages[$key]) || ! in_array($message, $messages[$key]);
+ }
+
+ /**
+ * Get the messages for a wildcard key.
+ */
+ protected function getMessagesForWildcardKey(string $key, ?string $format): array
+ {
+ return collect($this->messages)
+ ->filter(function ($messages, $messageKey) use ($key) {
+ return Str::is($key, $messageKey);
+ })
+ ->map(function ($messages, $messageKey) use ($format) {
+ return $this->transform(
+ $messages,
+ $this->checkFormat($format),
+ $messageKey
+ );
+ })->all();
+ }
+
+ /**
+ * Format an array of messages.
+ */
+ protected function transform(array $messages, string $format, string $messageKey): array
+ {
+ return collect($messages)
+ ->map(function ($message) use ($format, $messageKey) {
+ // We will simply spin through the given messages and transform each one
+ // replacing the :message place holder with the real message allowing
+ // the messages to be easily formatted to each developer's desires.
+ return str_replace([':message', ':key'], [$message, $messageKey], $format);
+ })->all();
+ }
+
+ /**
+ * Get the appropriate format based on the given format.
+ */
+ protected function checkFormat(?string $format): string
+ {
+ return $format ?: $this->format;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/MimeTypeExtensionGuesser.php b/server/addons/epay/library/hyperf/utils/src/MimeTypeExtensionGuesser.php
new file mode 100644
index 0000000..978c48e
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/MimeTypeExtensionGuesser.php
@@ -0,0 +1,814 @@
+ 'ez',
+ 'application/applixware' => 'aw',
+ 'application/atom+xml' => 'atom',
+ 'application/atomcat+xml' => 'atomcat',
+ 'application/atomsvc+xml' => 'atomsvc',
+ 'application/ccxml+xml' => 'ccxml',
+ 'application/cdmi-capability' => 'cdmia',
+ 'application/cdmi-container' => 'cdmic',
+ 'application/cdmi-domain' => 'cdmid',
+ 'application/cdmi-object' => 'cdmio',
+ 'application/cdmi-queue' => 'cdmiq',
+ 'application/cu-seeme' => 'cu',
+ 'application/davmount+xml' => 'davmount',
+ 'application/docbook+xml' => 'dbk',
+ 'application/dssc+der' => 'dssc',
+ 'application/dssc+xml' => 'xdssc',
+ 'application/ecmascript' => 'ecma',
+ 'application/emma+xml' => 'emma',
+ 'application/epub+zip' => 'epub',
+ 'application/exi' => 'exi',
+ 'application/font-tdpfr' => 'pfr',
+ 'application/gml+xml' => 'gml',
+ 'application/gpx+xml' => 'gpx',
+ 'application/gxf' => 'gxf',
+ 'application/hyperstudio' => 'stk',
+ 'application/inkml+xml' => 'ink',
+ 'application/ipfix' => 'ipfix',
+ 'application/java-archive' => 'jar',
+ 'application/java-serialized-object' => 'ser',
+ 'application/java-vm' => 'class',
+ 'application/javascript' => 'js',
+ 'application/json' => 'json',
+ 'application/jsonml+json' => 'jsonml',
+ 'application/lost+xml' => 'lostxml',
+ 'application/mac-binhex40' => 'hqx',
+ 'application/mac-compactpro' => 'cpt',
+ 'application/mads+xml' => 'mads',
+ 'application/marc' => 'mrc',
+ 'application/marcxml+xml' => 'mrcx',
+ 'application/mathematica' => 'ma',
+ 'application/mathml+xml' => 'mathml',
+ 'application/mbox' => 'mbox',
+ 'application/mediaservercontrol+xml' => 'mscml',
+ 'application/metalink+xml' => 'metalink',
+ 'application/metalink4+xml' => 'meta4',
+ 'application/mets+xml' => 'mets',
+ 'application/mods+xml' => 'mods',
+ 'application/mp21' => 'm21',
+ 'application/mp4' => 'mp4s',
+ 'application/msword' => 'doc',
+ 'application/mxf' => 'mxf',
+ 'application/octet-stream' => 'bin',
+ 'application/oda' => 'oda',
+ 'application/oebps-package+xml' => 'opf',
+ 'application/ogg' => 'ogx',
+ 'application/omdoc+xml' => 'omdoc',
+ 'application/onenote' => 'onetoc',
+ 'application/oxps' => 'oxps',
+ 'application/patch-ops-error+xml' => 'xer',
+ 'application/pdf' => 'pdf',
+ 'application/pgp-encrypted' => 'pgp',
+ 'application/pgp-signature' => 'asc',
+ 'application/pics-rules' => 'prf',
+ 'application/pkcs10' => 'p10',
+ 'application/pkcs7-mime' => 'p7m',
+ 'application/pkcs7-signature' => 'p7s',
+ 'application/pkcs8' => 'p8',
+ 'application/pkix-attr-cert' => 'ac',
+ 'application/pkix-cert' => 'cer',
+ 'application/pkix-crl' => 'crl',
+ 'application/pkix-pkipath' => 'pkipath',
+ 'application/pkixcmp' => 'pki',
+ 'application/pls+xml' => 'pls',
+ 'application/postscript' => 'ai',
+ 'application/prs.cww' => 'cww',
+ 'application/pskc+xml' => 'pskcxml',
+ 'application/rdf+xml' => 'rdf',
+ 'application/reginfo+xml' => 'rif',
+ 'application/relax-ng-compact-syntax' => 'rnc',
+ 'application/resource-lists+xml' => 'rl',
+ 'application/resource-lists-diff+xml' => 'rld',
+ 'application/rls-services+xml' => 'rs',
+ 'application/rpki-ghostbusters' => 'gbr',
+ 'application/rpki-manifest' => 'mft',
+ 'application/rpki-roa' => 'roa',
+ 'application/rsd+xml' => 'rsd',
+ 'application/rss+xml' => 'rss',
+ 'application/rtf' => 'rtf',
+ 'application/sbml+xml' => 'sbml',
+ 'application/scvp-cv-request' => 'scq',
+ 'application/scvp-cv-response' => 'scs',
+ 'application/scvp-vp-request' => 'spq',
+ 'application/scvp-vp-response' => 'spp',
+ 'application/sdp' => 'sdp',
+ 'application/set-payment-initiation' => 'setpay',
+ 'application/set-registration-initiation' => 'setreg',
+ 'application/shf+xml' => 'shf',
+ 'application/smil+xml' => 'smi',
+ 'application/sparql-query' => 'rq',
+ 'application/sparql-results+xml' => 'srx',
+ 'application/srgs' => 'gram',
+ 'application/srgs+xml' => 'grxml',
+ 'application/sru+xml' => 'sru',
+ 'application/ssdl+xml' => 'ssdl',
+ 'application/ssml+xml' => 'ssml',
+ 'application/tei+xml' => 'tei',
+ 'application/thraud+xml' => 'tfi',
+ 'application/timestamped-data' => 'tsd',
+ 'application/vnd.3gpp.pic-bw-large' => 'plb',
+ 'application/vnd.3gpp.pic-bw-small' => 'psb',
+ 'application/vnd.3gpp.pic-bw-var' => 'pvb',
+ 'application/vnd.3gpp2.tcap' => 'tcap',
+ 'application/vnd.3m.post-it-notes' => 'pwn',
+ 'application/vnd.accpac.simply.aso' => 'aso',
+ 'application/vnd.accpac.simply.imp' => 'imp',
+ 'application/vnd.acucobol' => 'acu',
+ 'application/vnd.acucorp' => 'atc',
+ 'application/vnd.adobe.air-application-installer-package+zip' => 'air',
+ 'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
+ 'application/vnd.adobe.fxp' => 'fxp',
+ 'application/vnd.adobe.xdp+xml' => 'xdp',
+ 'application/vnd.adobe.xfdf' => 'xfdf',
+ 'application/vnd.ahead.space' => 'ahead',
+ 'application/vnd.airzip.filesecure.azf' => 'azf',
+ 'application/vnd.airzip.filesecure.azs' => 'azs',
+ 'application/vnd.amazon.ebook' => 'azw',
+ 'application/vnd.americandynamics.acc' => 'acc',
+ 'application/vnd.amiga.ami' => 'ami',
+ 'application/vnd.android.package-archive' => 'apk',
+ 'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
+ 'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
+ 'application/vnd.antix.game-component' => 'atx',
+ 'application/vnd.apple.installer+xml' => 'mpkg',
+ 'application/vnd.apple.mpegurl' => 'm3u8',
+ 'application/vnd.aristanetworks.swi' => 'swi',
+ 'application/vnd.astraea-software.iota' => 'iota',
+ 'application/vnd.audiograph' => 'aep',
+ 'application/vnd.blueice.multipass' => 'mpm',
+ 'application/vnd.bmi' => 'bmi',
+ 'application/vnd.businessobjects' => 'rep',
+ 'application/vnd.chemdraw+xml' => 'cdxml',
+ 'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
+ 'application/vnd.cinderella' => 'cdy',
+ 'application/vnd.claymore' => 'cla',
+ 'application/vnd.cloanto.rp9' => 'rp9',
+ 'application/vnd.clonk.c4group' => 'c4g',
+ 'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
+ 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
+ 'application/vnd.commonspace' => 'csp',
+ 'application/vnd.contact.cmsg' => 'cdbcmsg',
+ 'application/vnd.cosmocaller' => 'cmc',
+ 'application/vnd.crick.clicker' => 'clkx',
+ 'application/vnd.crick.clicker.keyboard' => 'clkk',
+ 'application/vnd.crick.clicker.palette' => 'clkp',
+ 'application/vnd.crick.clicker.template' => 'clkt',
+ 'application/vnd.crick.clicker.wordbank' => 'clkw',
+ 'application/vnd.criticaltools.wbs+xml' => 'wbs',
+ 'application/vnd.ctc-posml' => 'pml',
+ 'application/vnd.cups-ppd' => 'ppd',
+ 'application/vnd.curl.car' => 'car',
+ 'application/vnd.curl.pcurl' => 'pcurl',
+ 'application/vnd.dart' => 'dart',
+ 'application/vnd.data-vision.rdz' => 'rdz',
+ 'application/vnd.dece.data' => 'uvf',
+ 'application/vnd.dece.ttml+xml' => 'uvt',
+ 'application/vnd.dece.unspecified' => 'uvx',
+ 'application/vnd.dece.zip' => 'uvz',
+ 'application/vnd.denovo.fcselayout-link' => 'fe_launch',
+ 'application/vnd.dna' => 'dna',
+ 'application/vnd.dolby.mlp' => 'mlp',
+ 'application/vnd.dpgraph' => 'dpg',
+ 'application/vnd.dreamfactory' => 'dfac',
+ 'application/vnd.ds-keypoint' => 'kpxx',
+ 'application/vnd.dvb.ait' => 'ait',
+ 'application/vnd.dvb.service' => 'svc',
+ 'application/vnd.dynageo' => 'geo',
+ 'application/vnd.ecowin.chart' => 'mag',
+ 'application/vnd.enliven' => 'nml',
+ 'application/vnd.epson.esf' => 'esf',
+ 'application/vnd.epson.msf' => 'msf',
+ 'application/vnd.epson.quickanime' => 'qam',
+ 'application/vnd.epson.salt' => 'slt',
+ 'application/vnd.epson.ssf' => 'ssf',
+ 'application/vnd.eszigno3+xml' => 'es3',
+ 'application/vnd.ezpix-album' => 'ez2',
+ 'application/vnd.ezpix-package' => 'ez3',
+ 'application/vnd.fdf' => 'fdf',
+ 'application/vnd.fdsn.mseed' => 'mseed',
+ 'application/vnd.fdsn.seed' => 'seed',
+ 'application/vnd.flographit' => 'gph',
+ 'application/vnd.fluxtime.clip' => 'ftc',
+ 'application/vnd.framemaker' => 'fm',
+ 'application/vnd.frogans.fnc' => 'fnc',
+ 'application/vnd.frogans.ltf' => 'ltf',
+ 'application/vnd.fsc.weblaunch' => 'fsc',
+ 'application/vnd.fujitsu.oasys' => 'oas',
+ 'application/vnd.fujitsu.oasys2' => 'oa2',
+ 'application/vnd.fujitsu.oasys3' => 'oa3',
+ 'application/vnd.fujitsu.oasysgp' => 'fg5',
+ 'application/vnd.fujitsu.oasysprs' => 'bh2',
+ 'application/vnd.fujixerox.ddd' => 'ddd',
+ 'application/vnd.fujixerox.docuworks' => 'xdw',
+ 'application/vnd.fujixerox.docuworks.binder' => 'xbd',
+ 'application/vnd.fuzzysheet' => 'fzs',
+ 'application/vnd.genomatix.tuxedo' => 'txd',
+ 'application/vnd.geogebra.file' => 'ggb',
+ 'application/vnd.geogebra.tool' => 'ggt',
+ 'application/vnd.geometry-explorer' => 'gex',
+ 'application/vnd.geonext' => 'gxt',
+ 'application/vnd.geoplan' => 'g2w',
+ 'application/vnd.geospace' => 'g3w',
+ 'application/vnd.gmx' => 'gmx',
+ 'application/vnd.google-earth.kml+xml' => 'kml',
+ 'application/vnd.google-earth.kmz' => 'kmz',
+ 'application/vnd.grafeq' => 'gqf',
+ 'application/vnd.groove-account' => 'gac',
+ 'application/vnd.groove-help' => 'ghf',
+ 'application/vnd.groove-identity-message' => 'gim',
+ 'application/vnd.groove-injector' => 'grv',
+ 'application/vnd.groove-tool-message' => 'gtm',
+ 'application/vnd.groove-tool-template' => 'tpl',
+ 'application/vnd.groove-vcard' => 'vcg',
+ 'application/vnd.hal+xml' => 'hal',
+ 'application/vnd.handheld-entertainment+xml' => 'zmm',
+ 'application/vnd.hbci' => 'hbci',
+ 'application/vnd.hhe.lesson-player' => 'les',
+ 'application/vnd.hp-hpgl' => 'hpgl',
+ 'application/vnd.hp-hpid' => 'hpid',
+ 'application/vnd.hp-hps' => 'hps',
+ 'application/vnd.hp-jlyt' => 'jlt',
+ 'application/vnd.hp-pcl' => 'pcl',
+ 'application/vnd.hp-pclxl' => 'pclxl',
+ 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
+ 'application/vnd.ibm.minipay' => 'mpy',
+ 'application/vnd.ibm.modcap' => 'afp',
+ 'application/vnd.ibm.rights-management' => 'irm',
+ 'application/vnd.ibm.secure-container' => 'sc',
+ 'application/vnd.iccprofile' => 'icc',
+ 'application/vnd.igloader' => 'igl',
+ 'application/vnd.immervision-ivp' => 'ivp',
+ 'application/vnd.immervision-ivu' => 'ivu',
+ 'application/vnd.insors.igm' => 'igm',
+ 'application/vnd.intercon.formnet' => 'xpw',
+ 'application/vnd.intergeo' => 'i2g',
+ 'application/vnd.intu.qbo' => 'qbo',
+ 'application/vnd.intu.qfx' => 'qfx',
+ 'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
+ 'application/vnd.irepository.package+xml' => 'irp',
+ 'application/vnd.is-xpr' => 'xpr',
+ 'application/vnd.isac.fcs' => 'fcs',
+ 'application/vnd.jam' => 'jam',
+ 'application/vnd.jcp.javame.midlet-rms' => 'rms',
+ 'application/vnd.jisp' => 'jisp',
+ 'application/vnd.joost.joda-archive' => 'joda',
+ 'application/vnd.kahootz' => 'ktz',
+ 'application/vnd.kde.karbon' => 'karbon',
+ 'application/vnd.kde.kchart' => 'chrt',
+ 'application/vnd.kde.kformula' => 'kfo',
+ 'application/vnd.kde.kivio' => 'flw',
+ 'application/vnd.kde.kontour' => 'kon',
+ 'application/vnd.kde.kpresenter' => 'kpr',
+ 'application/vnd.kde.kspread' => 'ksp',
+ 'application/vnd.kde.kword' => 'kwd',
+ 'application/vnd.kenameaapp' => 'htke',
+ 'application/vnd.kidspiration' => 'kia',
+ 'application/vnd.kinar' => 'kne',
+ 'application/vnd.koan' => 'skp',
+ 'application/vnd.kodak-descriptor' => 'sse',
+ 'application/vnd.las.las+xml' => 'lasxml',
+ 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
+ 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
+ 'application/vnd.lotus-1-2-3' => '123',
+ 'application/vnd.lotus-approach' => 'apr',
+ 'application/vnd.lotus-freelance' => 'pre',
+ 'application/vnd.lotus-notes' => 'nsf',
+ 'application/vnd.lotus-organizer' => 'org',
+ 'application/vnd.lotus-screencam' => 'scm',
+ 'application/vnd.lotus-wordpro' => 'lwp',
+ 'application/vnd.macports.portpkg' => 'portpkg',
+ 'application/vnd.mcd' => 'mcd',
+ 'application/vnd.medcalcdata' => 'mc1',
+ 'application/vnd.mediastation.cdkey' => 'cdkey',
+ 'application/vnd.mfer' => 'mwf',
+ 'application/vnd.mfmp' => 'mfm',
+ 'application/vnd.micrografx.flo' => 'flo',
+ 'application/vnd.micrografx.igx' => 'igx',
+ 'application/vnd.mif' => 'mif',
+ 'application/vnd.mobius.daf' => 'daf',
+ 'application/vnd.mobius.dis' => 'dis',
+ 'application/vnd.mobius.mbk' => 'mbk',
+ 'application/vnd.mobius.mqy' => 'mqy',
+ 'application/vnd.mobius.msl' => 'msl',
+ 'application/vnd.mobius.plc' => 'plc',
+ 'application/vnd.mobius.txf' => 'txf',
+ 'application/vnd.mophun.application' => 'mpn',
+ 'application/vnd.mophun.certificate' => 'mpc',
+ 'application/vnd.mozilla.xul+xml' => 'xul',
+ 'application/vnd.ms-artgalry' => 'cil',
+ 'application/vnd.ms-cab-compressed' => 'cab',
+ 'application/vnd.ms-excel' => 'xls',
+ 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
+ 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
+ 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
+ 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
+ 'application/vnd.ms-fontobject' => 'eot',
+ 'application/vnd.ms-htmlhelp' => 'chm',
+ 'application/vnd.ms-ims' => 'ims',
+ 'application/vnd.ms-lrm' => 'lrm',
+ 'application/vnd.ms-officetheme' => 'thmx',
+ 'application/vnd.ms-pki.seccat' => 'cat',
+ 'application/vnd.ms-pki.stl' => 'stl',
+ 'application/vnd.ms-powerpoint' => 'ppt',
+ 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
+ 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
+ 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
+ 'application/vnd.ms-project' => 'mpp',
+ 'application/vnd.ms-word.document.macroenabled.12' => 'docm',
+ 'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
+ 'application/vnd.ms-works' => 'wps',
+ 'application/vnd.ms-wpl' => 'wpl',
+ 'application/vnd.ms-xpsdocument' => 'xps',
+ 'application/vnd.mseq' => 'mseq',
+ 'application/vnd.musician' => 'mus',
+ 'application/vnd.muvee.style' => 'msty',
+ 'application/vnd.mynfc' => 'taglet',
+ 'application/vnd.neurolanguage.nlu' => 'nlu',
+ 'application/vnd.nitf' => 'ntf',
+ 'application/vnd.noblenet-directory' => 'nnd',
+ 'application/vnd.noblenet-sealer' => 'nns',
+ 'application/vnd.noblenet-web' => 'nnw',
+ 'application/vnd.nokia.n-gage.data' => 'ngdat',
+ 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
+ 'application/vnd.nokia.radio-preset' => 'rpst',
+ 'application/vnd.nokia.radio-presets' => 'rpss',
+ 'application/vnd.novadigm.edm' => 'edm',
+ 'application/vnd.novadigm.edx' => 'edx',
+ 'application/vnd.novadigm.ext' => 'ext',
+ 'application/vnd.oasis.opendocument.chart' => 'odc',
+ 'application/vnd.oasis.opendocument.chart-template' => 'otc',
+ 'application/vnd.oasis.opendocument.database' => 'odb',
+ 'application/vnd.oasis.opendocument.formula' => 'odf',
+ 'application/vnd.oasis.opendocument.formula-template' => 'odft',
+ 'application/vnd.oasis.opendocument.graphics' => 'odg',
+ 'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+ 'application/vnd.oasis.opendocument.image' => 'odi',
+ 'application/vnd.oasis.opendocument.image-template' => 'oti',
+ 'application/vnd.oasis.opendocument.presentation' => 'odp',
+ 'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+ 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+ 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+ 'application/vnd.oasis.opendocument.text' => 'odt',
+ 'application/vnd.oasis.opendocument.text-master' => 'odm',
+ 'application/vnd.oasis.opendocument.text-template' => 'ott',
+ 'application/vnd.oasis.opendocument.text-web' => 'oth',
+ 'application/vnd.olpc-sugar' => 'xo',
+ 'application/vnd.oma.dd2+xml' => 'dd2',
+ 'application/vnd.openofficeorg.extension' => 'oxt',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
+ 'application/vnd.osgeo.mapguide.package' => 'mgp',
+ 'application/vnd.osgi.dp' => 'dp',
+ 'application/vnd.osgi.subsystem' => 'esa',
+ 'application/vnd.palm' => 'pdb',
+ 'application/vnd.pawaafile' => 'paw',
+ 'application/vnd.pg.format' => 'str',
+ 'application/vnd.pg.osasli' => 'ei6',
+ 'application/vnd.picsel' => 'efif',
+ 'application/vnd.pmi.widget' => 'wg',
+ 'application/vnd.pocketlearn' => 'plf',
+ 'application/vnd.powerbuilder6' => 'pbd',
+ 'application/vnd.previewsystems.box' => 'box',
+ 'application/vnd.proteus.magazine' => 'mgz',
+ 'application/vnd.publishare-delta-tree' => 'qps',
+ 'application/vnd.pvi.ptid1' => 'ptid',
+ 'application/vnd.quark.quarkxpress' => 'qxd',
+ 'application/vnd.realvnc.bed' => 'bed',
+ 'application/vnd.recordare.musicxml' => 'mxl',
+ 'application/vnd.recordare.musicxml+xml' => 'musicxml',
+ 'application/vnd.rig.cryptonote' => 'cryptonote',
+ 'application/vnd.rim.cod' => 'cod',
+ 'application/vnd.rn-realmedia' => 'rm',
+ 'application/vnd.rn-realmedia-vbr' => 'rmvb',
+ 'application/vnd.route66.link66+xml' => 'link66',
+ 'application/vnd.sailingtracker.track' => 'st',
+ 'application/vnd.seemail' => 'see',
+ 'application/vnd.sema' => 'sema',
+ 'application/vnd.semd' => 'semd',
+ 'application/vnd.semf' => 'semf',
+ 'application/vnd.shana.informed.formdata' => 'ifm',
+ 'application/vnd.shana.informed.formtemplate' => 'itp',
+ 'application/vnd.shana.informed.interchange' => 'iif',
+ 'application/vnd.shana.informed.package' => 'ipk',
+ 'application/vnd.simtech-mindmapper' => 'twd',
+ 'application/vnd.smaf' => 'mmf',
+ 'application/vnd.smart.teacher' => 'teacher',
+ 'application/vnd.solent.sdkm+xml' => 'sdkm',
+ 'application/vnd.spotfire.dxp' => 'dxp',
+ 'application/vnd.spotfire.sfs' => 'sfs',
+ 'application/vnd.stardivision.calc' => 'sdc',
+ 'application/vnd.stardivision.draw' => 'sda',
+ 'application/vnd.stardivision.impress' => 'sdd',
+ 'application/vnd.stardivision.math' => 'smf',
+ 'application/vnd.stardivision.writer' => 'sdw',
+ 'application/vnd.stardivision.writer-global' => 'sgl',
+ 'application/vnd.stepmania.package' => 'smzip',
+ 'application/vnd.stepmania.stepchart' => 'sm',
+ 'application/vnd.sun.xml.calc' => 'sxc',
+ 'application/vnd.sun.xml.calc.template' => 'stc',
+ 'application/vnd.sun.xml.draw' => 'sxd',
+ 'application/vnd.sun.xml.draw.template' => 'std',
+ 'application/vnd.sun.xml.impress' => 'sxi',
+ 'application/vnd.sun.xml.impress.template' => 'sti',
+ 'application/vnd.sun.xml.math' => 'sxm',
+ 'application/vnd.sun.xml.writer' => 'sxw',
+ 'application/vnd.sun.xml.writer.global' => 'sxg',
+ 'application/vnd.sun.xml.writer.template' => 'stw',
+ 'application/vnd.sus-calendar' => 'sus',
+ 'application/vnd.svd' => 'svd',
+ 'application/vnd.symbian.install' => 'sis',
+ 'application/vnd.syncml+xml' => 'xsm',
+ 'application/vnd.syncml.dm+wbxml' => 'bdm',
+ 'application/vnd.syncml.dm+xml' => 'xdm',
+ 'application/vnd.tao.intent-module-archive' => 'tao',
+ 'application/vnd.tcpdump.pcap' => 'pcap',
+ 'application/vnd.tmobile-livetv' => 'tmo',
+ 'application/vnd.trid.tpt' => 'tpt',
+ 'application/vnd.triscape.mxs' => 'mxs',
+ 'application/vnd.trueapp' => 'tra',
+ 'application/vnd.ufdl' => 'ufd',
+ 'application/vnd.uiq.theme' => 'utz',
+ 'application/vnd.umajin' => 'umj',
+ 'application/vnd.unity' => 'unityweb',
+ 'application/vnd.uoml+xml' => 'uoml',
+ 'application/vnd.vcx' => 'vcx',
+ 'application/vnd.visio' => 'vsd',
+ 'application/vnd.visionary' => 'vis',
+ 'application/vnd.vsf' => 'vsf',
+ 'application/vnd.wap.wbxml' => 'wbxml',
+ 'application/vnd.wap.wmlc' => 'wmlc',
+ 'application/vnd.wap.wmlscriptc' => 'wmlsc',
+ 'application/vnd.webturbo' => 'wtb',
+ 'application/vnd.wolfram.player' => 'nbp',
+ 'application/vnd.wordperfect' => 'wpd',
+ 'application/vnd.wqd' => 'wqd',
+ 'application/vnd.wt.stf' => 'stf',
+ 'application/vnd.xara' => 'xar',
+ 'application/vnd.xfdl' => 'xfdl',
+ 'application/vnd.yamaha.hv-dic' => 'hvd',
+ 'application/vnd.yamaha.hv-script' => 'hvs',
+ 'application/vnd.yamaha.hv-voice' => 'hvp',
+ 'application/vnd.yamaha.openscoreformat' => 'osf',
+ 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
+ 'application/vnd.yamaha.smaf-audio' => 'saf',
+ 'application/vnd.yamaha.smaf-phrase' => 'spf',
+ 'application/vnd.yellowriver-custom-menu' => 'cmp',
+ 'application/vnd.zul' => 'zir',
+ 'application/vnd.zzazz.deck+xml' => 'zaz',
+ 'application/voicexml+xml' => 'vxml',
+ 'application/widget' => 'wgt',
+ 'application/winhlp' => 'hlp',
+ 'application/wsdl+xml' => 'wsdl',
+ 'application/wspolicy+xml' => 'wspolicy',
+ 'application/x-7z-compressed' => '7z',
+ 'application/x-abiword' => 'abw',
+ 'application/x-ace-compressed' => 'ace',
+ 'application/x-apple-diskimage' => 'dmg',
+ 'application/x-authorware-bin' => 'aab',
+ 'application/x-authorware-map' => 'aam',
+ 'application/x-authorware-seg' => 'aas',
+ 'application/x-bcpio' => 'bcpio',
+ 'application/x-bittorrent' => 'torrent',
+ 'application/x-blorb' => 'blb',
+ 'application/x-bzip' => 'bz',
+ 'application/x-bzip2' => 'bz2',
+ 'application/x-cbr' => 'cbr',
+ 'application/x-cdlink' => 'vcd',
+ 'application/x-cfs-compressed' => 'cfs',
+ 'application/x-chat' => 'chat',
+ 'application/x-chess-pgn' => 'pgn',
+ 'application/x-conference' => 'nsc',
+ 'application/x-cpio' => 'cpio',
+ 'application/x-csh' => 'csh',
+ 'application/x-debian-package' => 'deb',
+ 'application/x-dgc-compressed' => 'dgc',
+ 'application/x-director' => 'dir',
+ 'application/x-doom' => 'wad',
+ 'application/x-dtbncx+xml' => 'ncx',
+ 'application/x-dtbook+xml' => 'dtb',
+ 'application/x-dtbresource+xml' => 'res',
+ 'application/x-dvi' => 'dvi',
+ 'application/x-envoy' => 'evy',
+ 'application/x-eva' => 'eva',
+ 'application/x-font-bdf' => 'bdf',
+ 'application/x-font-ghostscript' => 'gsf',
+ 'application/x-font-linux-psf' => 'psf',
+ 'application/x-font-otf' => 'otf',
+ 'application/x-font-pcf' => 'pcf',
+ 'application/x-font-snf' => 'snf',
+ 'application/x-font-ttf' => 'ttf',
+ 'application/x-font-type1' => 'pfa',
+ 'application/x-font-woff' => 'woff',
+ 'application/x-freearc' => 'arc',
+ 'application/x-futuresplash' => 'spl',
+ 'application/x-gca-compressed' => 'gca',
+ 'application/x-glulx' => 'ulx',
+ 'application/x-gnumeric' => 'gnumeric',
+ 'application/x-gramps-xml' => 'gramps',
+ 'application/x-gtar' => 'gtar',
+ 'application/x-hdf' => 'hdf',
+ 'application/x-install-instructions' => 'install',
+ 'application/x-iso9660-image' => 'iso',
+ 'application/x-java-jnlp-file' => 'jnlp',
+ 'application/x-latex' => 'latex',
+ 'application/x-lzh-compressed' => 'lzh',
+ 'application/x-mie' => 'mie',
+ 'application/x-mobipocket-ebook' => 'prc',
+ 'application/x-ms-application' => 'application',
+ 'application/x-ms-shortcut' => 'lnk',
+ 'application/x-ms-wmd' => 'wmd',
+ 'application/x-ms-wmz' => 'wmz',
+ 'application/x-ms-xbap' => 'xbap',
+ 'application/x-msaccess' => 'mdb',
+ 'application/x-msbinder' => 'obd',
+ 'application/x-mscardfile' => 'crd',
+ 'application/x-msclip' => 'clp',
+ 'application/x-msdownload' => 'exe',
+ 'application/x-msmediaview' => 'mvb',
+ 'application/x-msmetafile' => 'wmf',
+ 'application/x-msmoney' => 'mny',
+ 'application/x-mspublisher' => 'pub',
+ 'application/x-msschedule' => 'scd',
+ 'application/x-msterminal' => 'trm',
+ 'application/x-mswrite' => 'wri',
+ 'application/x-netcdf' => 'nc',
+ 'application/x-nzb' => 'nzb',
+ 'application/x-pkcs12' => 'p12',
+ 'application/x-pkcs7-certificates' => 'p7b',
+ 'application/x-pkcs7-certreqresp' => 'p7r',
+ 'application/x-rar-compressed' => 'rar',
+ 'application/x-rar' => 'rar',
+ 'application/x-research-info-systems' => 'ris',
+ 'application/x-sh' => 'sh',
+ 'application/x-shar' => 'shar',
+ 'application/x-shockwave-flash' => 'swf',
+ 'application/x-silverlight-app' => 'xap',
+ 'application/x-sql' => 'sql',
+ 'application/x-stuffit' => 'sit',
+ 'application/x-stuffitx' => 'sitx',
+ 'application/x-subrip' => 'srt',
+ 'application/x-sv4cpio' => 'sv4cpio',
+ 'application/x-sv4crc' => 'sv4crc',
+ 'application/x-t3vm-image' => 't3',
+ 'application/x-tads' => 'gam',
+ 'application/x-tar' => 'tar',
+ 'application/x-tcl' => 'tcl',
+ 'application/x-tex' => 'tex',
+ 'application/x-tex-tfm' => 'tfm',
+ 'application/x-texinfo' => 'texinfo',
+ 'application/x-tgif' => 'obj',
+ 'application/x-ustar' => 'ustar',
+ 'application/x-wais-source' => 'src',
+ 'application/x-x509-ca-cert' => 'der',
+ 'application/x-xfig' => 'fig',
+ 'application/x-xliff+xml' => 'xlf',
+ 'application/x-xpinstall' => 'xpi',
+ 'application/x-xz' => 'xz',
+ 'application/x-zip-compressed' => 'zip',
+ 'application/x-zmachine' => 'z1',
+ 'application/xaml+xml' => 'xaml',
+ 'application/xcap-diff+xml' => 'xdf',
+ 'application/xenc+xml' => 'xenc',
+ 'application/xhtml+xml' => 'xhtml',
+ 'application/xml' => 'xml',
+ 'application/xml-dtd' => 'dtd',
+ 'application/xop+xml' => 'xop',
+ 'application/xproc+xml' => 'xpl',
+ 'application/xslt+xml' => 'xslt',
+ 'application/xspf+xml' => 'xspf',
+ 'application/xv+xml' => 'mxml',
+ 'application/yang' => 'yang',
+ 'application/yin+xml' => 'yin',
+ 'application/zip' => 'zip',
+ 'audio/adpcm' => 'adp',
+ 'audio/basic' => 'au',
+ 'audio/midi' => 'mid',
+ 'audio/mp4' => 'mp4a',
+ 'audio/mpeg' => 'mpga',
+ 'audio/ogg' => 'oga',
+ 'audio/s3m' => 's3m',
+ 'audio/silk' => 'sil',
+ 'audio/vnd.dece.audio' => 'uva',
+ 'audio/vnd.digital-winds' => 'eol',
+ 'audio/vnd.dra' => 'dra',
+ 'audio/vnd.dts' => 'dts',
+ 'audio/vnd.dts.hd' => 'dtshd',
+ 'audio/vnd.lucent.voice' => 'lvp',
+ 'audio/vnd.ms-playready.media.pya' => 'pya',
+ 'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
+ 'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
+ 'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
+ 'audio/vnd.rip' => 'rip',
+ 'audio/webm' => 'weba',
+ 'audio/x-aac' => 'aac',
+ 'audio/x-aiff' => 'aif',
+ 'audio/x-caf' => 'caf',
+ 'audio/x-flac' => 'flac',
+ 'audio/x-matroska' => 'mka',
+ 'audio/x-mpegurl' => 'm3u',
+ 'audio/x-ms-wax' => 'wax',
+ 'audio/x-ms-wma' => 'wma',
+ 'audio/x-pn-realaudio' => 'ram',
+ 'audio/x-pn-realaudio-plugin' => 'rmp',
+ 'audio/x-wav' => 'wav',
+ 'audio/xm' => 'xm',
+ 'chemical/x-cdx' => 'cdx',
+ 'chemical/x-cif' => 'cif',
+ 'chemical/x-cmdf' => 'cmdf',
+ 'chemical/x-cml' => 'cml',
+ 'chemical/x-csml' => 'csml',
+ 'chemical/x-xyz' => 'xyz',
+ 'image/bmp' => 'bmp',
+ 'image/x-ms-bmp' => 'bmp',
+ 'image/cgm' => 'cgm',
+ 'image/g3fax' => 'g3',
+ 'image/gif' => 'gif',
+ 'image/ief' => 'ief',
+ 'image/jpeg' => 'jpg',
+ 'image/pjpeg' => 'jpeg',
+ 'image/ktx' => 'ktx',
+ 'image/png' => 'png',
+ 'image/prs.btif' => 'btif',
+ 'image/sgi' => 'sgi',
+ 'image/svg+xml' => 'svg',
+ 'image/tiff' => 'tiff',
+ 'image/vnd.adobe.photoshop' => 'psd',
+ 'image/vnd.dece.graphic' => 'uvi',
+ 'image/vnd.dvb.subtitle' => 'sub',
+ 'image/vnd.djvu' => 'djvu',
+ 'image/vnd.dwg' => 'dwg',
+ 'image/vnd.dxf' => 'dxf',
+ 'image/vnd.fastbidsheet' => 'fbs',
+ 'image/vnd.fpx' => 'fpx',
+ 'image/vnd.fst' => 'fst',
+ 'image/vnd.fujixerox.edmics-mmr' => 'mmr',
+ 'image/vnd.fujixerox.edmics-rlc' => 'rlc',
+ 'image/vnd.ms-modi' => 'mdi',
+ 'image/vnd.ms-photo' => 'wdp',
+ 'image/vnd.net-fpx' => 'npx',
+ 'image/vnd.wap.wbmp' => 'wbmp',
+ 'image/vnd.xiff' => 'xif',
+ 'image/webp' => 'webp',
+ 'image/x-3ds' => '3ds',
+ 'image/x-cmu-raster' => 'ras',
+ 'image/x-cmx' => 'cmx',
+ 'image/x-freehand' => 'fh',
+ 'image/x-icon' => 'ico',
+ 'image/x-mrsid-image' => 'sid',
+ 'image/x-pcx' => 'pcx',
+ 'image/x-pict' => 'pic',
+ 'image/x-portable-anymap' => 'pnm',
+ 'image/x-portable-bitmap' => 'pbm',
+ 'image/x-portable-graymap' => 'pgm',
+ 'image/x-portable-pixmap' => 'ppm',
+ 'image/x-rgb' => 'rgb',
+ 'image/x-tga' => 'tga',
+ 'image/x-xbitmap' => 'xbm',
+ 'image/x-xpixmap' => 'xpm',
+ 'image/x-xwindowdump' => 'xwd',
+ 'message/rfc822' => 'eml',
+ 'model/iges' => 'igs',
+ 'model/mesh' => 'msh',
+ 'model/vnd.collada+xml' => 'dae',
+ 'model/vnd.dwf' => 'dwf',
+ 'model/vnd.gdl' => 'gdl',
+ 'model/vnd.gtw' => 'gtw',
+ 'model/vnd.mts' => 'mts',
+ 'model/vnd.vtu' => 'vtu',
+ 'model/vrml' => 'wrl',
+ 'model/x3d+binary' => 'x3db',
+ 'model/x3d+vrml' => 'x3dv',
+ 'model/x3d+xml' => 'x3d',
+ 'text/cache-manifest' => 'appcache',
+ 'text/calendar' => 'ics',
+ 'text/css' => 'css',
+ 'text/csv' => 'csv',
+ 'text/html' => 'html',
+ 'text/n3' => 'n3',
+ 'text/plain' => 'txt',
+ 'text/prs.lines.tag' => 'dsc',
+ 'text/richtext' => 'rtx',
+ 'text/rtf' => 'rtf',
+ 'text/sgml' => 'sgml',
+ 'text/tab-separated-values' => 'tsv',
+ 'text/troff' => 't',
+ 'text/turtle' => 'ttl',
+ 'text/uri-list' => 'uri',
+ 'text/vcard' => 'vcard',
+ 'text/vnd.curl' => 'curl',
+ 'text/vnd.curl.dcurl' => 'dcurl',
+ 'text/vnd.curl.scurl' => 'scurl',
+ 'text/vnd.curl.mcurl' => 'mcurl',
+ 'text/vnd.dvb.subtitle' => 'sub',
+ 'text/vnd.fly' => 'fly',
+ 'text/vnd.fmi.flexstor' => 'flx',
+ 'text/vnd.graphviz' => 'gv',
+ 'text/vnd.in3d.3dml' => '3dml',
+ 'text/vnd.in3d.spot' => 'spot',
+ 'text/vnd.sun.j2me.app-descriptor' => 'jad',
+ 'text/vnd.wap.wml' => 'wml',
+ 'text/vnd.wap.wmlscript' => 'wmls',
+ 'text/vtt' => 'vtt',
+ 'text/x-asm' => 's',
+ 'text/x-c' => 'c',
+ 'text/x-fortran' => 'f',
+ 'text/x-pascal' => 'p',
+ 'text/x-java-source' => 'java',
+ 'text/x-opml' => 'opml',
+ 'text/x-nfo' => 'nfo',
+ 'text/x-setext' => 'etx',
+ 'text/x-sfv' => 'sfv',
+ 'text/x-uuencode' => 'uu',
+ 'text/x-vcalendar' => 'vcs',
+ 'text/x-vcard' => 'vcf',
+ 'video/3gpp' => '3gp',
+ 'video/3gpp2' => '3g2',
+ 'video/h261' => 'h261',
+ 'video/h263' => 'h263',
+ 'video/h264' => 'h264',
+ 'video/jpeg' => 'jpgv',
+ 'video/jpm' => 'jpm',
+ 'video/mj2' => 'mj2',
+ 'video/mp4' => 'mp4',
+ 'video/mpeg' => 'mpeg',
+ 'video/ogg' => 'ogv',
+ 'video/quicktime' => 'qt',
+ 'video/vnd.dece.hd' => 'uvh',
+ 'video/vnd.dece.mobile' => 'uvm',
+ 'video/vnd.dece.pd' => 'uvp',
+ 'video/vnd.dece.sd' => 'uvs',
+ 'video/vnd.dece.video' => 'uvv',
+ 'video/vnd.dvb.file' => 'dvb',
+ 'video/vnd.fvt' => 'fvt',
+ 'video/vnd.mpegurl' => 'mxu',
+ 'video/vnd.ms-playready.media.pyv' => 'pyv',
+ 'video/vnd.uvvu.mp4' => 'uvu',
+ 'video/vnd.vivo' => 'viv',
+ 'video/webm' => 'webm',
+ 'video/x-f4v' => 'f4v',
+ 'video/x-fli' => 'fli',
+ 'video/x-flv' => 'flv',
+ 'video/x-m4v' => 'm4v',
+ 'video/x-matroska' => 'mkv',
+ 'video/x-mng' => 'mng',
+ 'video/x-ms-asf' => 'asf',
+ 'video/x-ms-vob' => 'vob',
+ 'video/x-ms-wm' => 'wm',
+ 'video/x-ms-wmv' => 'wmv',
+ 'video/x-ms-wmx' => 'wmx',
+ 'video/x-ms-wvx' => 'wvx',
+ 'video/x-msvideo' => 'avi',
+ 'video/x-sgi-movie' => 'movie',
+ 'video/x-smv' => 'smv',
+ 'x-conference/x-cooltalk' => 'ice',
+ ];
+
+ /**
+ * Reversed from $defaultExtensions property in constructor.
+ */
+ private $defaultMineTypes = [];
+
+ public function __construct()
+ {
+ $this->defaultMineTypes = array_flip($this->defaultExtensions);
+ }
+
+ public function guessExtension(string $mimeType): ?string
+ {
+ return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
+ }
+
+ public function guessMimeType(string $extension): ?string
+ {
+ return isset($this->defaultMineTypes[$extension]) ? $this->defaultMineTypes[$extension] : null;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Network.php b/server/addons/epay/library/hyperf/utils/src/Network.php
new file mode 100644
index 0000000..16ed0d8
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Network.php
@@ -0,0 +1,46 @@
+ $value) {
+ foreach ($value['unicast'] as $item) {
+ if (! isset($item['address'])) {
+ continue;
+ }
+ if (! Str::contains($item['address'], '::') && $item['address'] !== '127.0.0.1') {
+ $ips[$name] = $item['address'];
+ }
+ }
+ }
+ }
+ if (is_array($ips) && ! empty($ips)) {
+ return current($ips);
+ }
+ /** @var mixed|string $ip */
+ $ip = gethostbyname(gethostname());
+ if (is_string($ip)) {
+ return $ip;
+ }
+ throw new RuntimeException('Can not get the internal IP.');
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Optional.php b/server/addons/epay/library/hyperf/utils/src/Optional.php
new file mode 100644
index 0000000..e990700
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Optional.php
@@ -0,0 +1,134 @@
+value = $value;
+ }
+
+ /**
+ * Dynamically access a property on the underlying object.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ if (is_object($this->value)) {
+ return $this->value->{$key} ?? null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Dynamically check a property exists on the underlying object.
+ *
+ * @param mixed $name
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ if (is_object($this->value)) {
+ return isset($this->value->{$name});
+ }
+
+ return false;
+ }
+
+ /**
+ * Dynamically pass a method to the underlying object.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return $this->macroCall($method, $parameters);
+ }
+
+ if (is_object($this->value)) {
+ return $this->value->{$method}(...$parameters);
+ }
+ }
+
+ /**
+ * Determine if an item exists at an offset.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function offsetExists($key)
+ {
+ return Arr::accessible($this->value) && Arr::exists($this->value, $key);
+ }
+
+ /**
+ * Get an item at a given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ public function offsetGet($key)
+ {
+ return Arr::get($this->value, $key);
+ }
+
+ /**
+ * Set the item at a given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ public function offsetSet($key, $value)
+ {
+ if (Arr::accessible($this->value)) {
+ $this->value[$key] = $value;
+ }
+ }
+
+ /**
+ * Unset the item at a given offset.
+ *
+ * @param string $key
+ */
+ public function offsetUnset($key)
+ {
+ if (Arr::accessible($this->value)) {
+ unset($this->value[$key]);
+ }
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Packer/JsonPacker.php b/server/addons/epay/library/hyperf/utils/src/Packer/JsonPacker.php
new file mode 100644
index 0000000..5fa8c98
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Packer/JsonPacker.php
@@ -0,0 +1,27 @@
+ 0) {
+ $this->concurrentChannel = new Channel($concurrent);
+ }
+ }
+
+ public function add(callable $callable, $key = null)
+ {
+ if (is_null($key)) {
+ $this->callbacks[] = $callable;
+ } else {
+ $this->callbacks[$key] = $callable;
+ }
+ }
+
+ public function wait(bool $throw = true): array
+ {
+ $wg = new WaitGroup();
+ $wg->add(count($this->callbacks));
+ foreach ($this->callbacks as $key => $callback) {
+ $this->concurrentChannel && $this->concurrentChannel->push(true);
+ $this->results[$key] = null;
+ Coroutine::create(function () use ($callback, $key, $wg) {
+ try {
+ $this->results[$key] = $callback();
+ } catch (\Throwable $throwable) {
+ $this->throwables[$key] = $throwable;
+ unset($this->results[$key]);
+ } finally {
+ $this->concurrentChannel && $this->concurrentChannel->pop();
+ $wg->done();
+ }
+ });
+ }
+ $wg->wait();
+ if ($throw && ($throwableCount = count($this->throwables)) > 0) {
+ $message = 'Detecting ' . $throwableCount . ' throwable occurred during parallel execution:' . PHP_EOL . $this->formatThrowables($this->throwables);
+ $executionException = new ParallelExecutionException($message);
+ $executionException->setResults($this->results);
+ $executionException->setThrowables($this->throwables);
+ unset($this->results, $this->throwables);
+ throw $executionException;
+ }
+ return $this->results;
+ }
+
+ public function count(): int
+ {
+ return count($this->callbacks);
+ }
+
+ public function clear(): void
+ {
+ $this->callbacks = [];
+ $this->results = [];
+ $this->throwables = [];
+ }
+
+ /**
+ * Format throwables into a nice list.
+ *
+ * @param \Throwable[] $throwables
+ */
+ private function formatThrowables(array $throwables): string
+ {
+ $output = '';
+ foreach ($throwables as $key => $value) {
+ $output .= \sprintf('(%s) %s: %s' . PHP_EOL . '%s' . PHP_EOL, $key, get_class($value), $value->getMessage(), $value->getTraceAsString());
+ }
+ return $output;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Pipeline.php b/server/addons/epay/library/hyperf/utils/src/Pipeline.php
new file mode 100644
index 0000000..217c19d
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Pipeline.php
@@ -0,0 +1,171 @@
+container = $container;
+ }
+
+ /**
+ * Set the object being sent through the pipeline.
+ * @param mixed $passable
+ */
+ public function send($passable): self
+ {
+ $this->passable = $passable;
+
+ return $this;
+ }
+
+ /**
+ * Set the array of pipes.
+ *
+ * @param array|mixed $pipes
+ */
+ public function through($pipes): self
+ {
+ $this->pipes = is_array($pipes) ? $pipes : func_get_args();
+ return $this;
+ }
+
+ /**
+ * Set the method to call on the pipes.
+ */
+ public function via(string $method): self
+ {
+ $this->method = $method;
+
+ return $this;
+ }
+
+ /**
+ * Run the pipeline with a final destination callback.
+ */
+ public function then(Closure $destination)
+ {
+ $pipeline = array_reduce(array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination));
+
+ return $pipeline($this->passable);
+ }
+
+ /**
+ * Get the final piece of the Closure onion.
+ */
+ protected function prepareDestination(Closure $destination): Closure
+ {
+ return static function ($passable) use ($destination) {
+ return $destination($passable);
+ };
+ }
+
+ /**
+ * Get a Closure that represents a slice of the application onion.
+ */
+ protected function carry(): Closure
+ {
+ return function ($stack, $pipe) {
+ return function ($passable) use ($stack, $pipe) {
+ if (is_callable($pipe)) {
+ // If the pipe is an instance of a Closure, we will just call it directly but
+ // otherwise we'll resolve the pipes out of the container and call it with
+ // the appropriate method and arguments, returning the results back out.
+ return $pipe($passable, $stack);
+ }
+ if (! is_object($pipe)) {
+ [$name, $parameters] = $this->parsePipeString($pipe);
+
+ // If the pipe is a string we will parse the string and resolve the class out
+ // of the dependency injection container. We can then build a callable and
+ // execute the pipe function giving in the parameters that are required.
+ $pipe = $this->container->get($name);
+
+ $parameters = array_merge([$passable, $stack], $parameters);
+ } else {
+ // If the pipe is already an object we'll just make a callable and pass it to
+ // the pipe as-is. There is no need to do any extra parsing and formatting
+ // since the object we're given was already a fully instantiated object.
+ $parameters = [$passable, $stack];
+ }
+
+ $carry = method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters);
+
+ return $this->handleCarry($carry);
+ };
+ };
+ }
+
+ /**
+ * Parse full pipe string to get name and parameters.
+ *
+ * @param string $pipe
+ * @return array
+ */
+ protected function parsePipeString($pipe)
+ {
+ [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
+
+ if (is_string($parameters)) {
+ $parameters = explode(',', $parameters);
+ }
+
+ return [$name, $parameters];
+ }
+
+ /**
+ * Handle the value returned from each pipe before passing it to the next.
+ *
+ * @param mixed $carry
+ * @return mixed
+ */
+ protected function handleCarry($carry)
+ {
+ return $carry;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Pluralizer.php b/server/addons/epay/library/hyperf/utils/src/Pluralizer.php
new file mode 100644
index 0000000..543291c
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Pluralizer.php
@@ -0,0 +1,161 @@
+pluralize($value);
+
+ return static::matchCase($plural, $value);
+ }
+
+ /**
+ * Get the singular form of an English word.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function singular($value)
+ {
+ $singular = static::getInflector()->singularize($value);
+
+ return static::matchCase($singular, $value);
+ }
+
+ public static function setInflector(?Inflector $inflector): void
+ {
+ static::$inflector = $inflector;
+ }
+
+ /**
+ * Get the inflector instance.
+ */
+ public static function getInflector(): Inflector
+ {
+ if (is_null(static::$inflector)) {
+ static::$inflector = new Inflector(
+ new CachedWordInflector(new RulesetInflector(
+ English\Rules::getSingularRuleset()
+ )),
+ new CachedWordInflector(new RulesetInflector(
+ English\Rules::getPluralRuleset()
+ ))
+ );
+ }
+
+ return static::$inflector;
+ }
+
+ /**
+ * Determine if the given value is uncountable.
+ *
+ * @param string $value
+ * @return bool
+ */
+ protected static function uncountable($value)
+ {
+ return in_array(strtolower($value), static::$uncountable);
+ }
+
+ /**
+ * Attempt to match the case on two strings.
+ *
+ * @param string $value
+ * @param string $comparison
+ * @return string
+ */
+ protected static function matchCase($value, $comparison)
+ {
+ $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
+
+ foreach ($functions as $function) {
+ if (call_user_func($function, $comparison) === $comparison) {
+ return call_user_func($function, $value);
+ }
+ }
+
+ return $value;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Reflection/ClassInvoker.php b/server/addons/epay/library/hyperf/utils/src/Reflection/ClassInvoker.php
new file mode 100644
index 0000000..4b413f8
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Reflection/ClassInvoker.php
@@ -0,0 +1,51 @@
+instance = $instance;
+ $this->reflection = new ReflectionClass($instance);
+ }
+
+ public function __get($name)
+ {
+ $property = $this->reflection->getProperty($name);
+
+ $property->setAccessible(true);
+
+ return $property->getValue($this->instance);
+ }
+
+ public function __call($name, $arguments)
+ {
+ $method = $this->reflection->getMethod($name);
+
+ $method->setAccessible(true);
+
+ return $method->invokeArgs($this->instance, $arguments);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Resource.php b/server/addons/epay/library/hyperf/utils/src/Resource.php
new file mode 100644
index 0000000..92aa822
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Resource.php
@@ -0,0 +1,37 @@
+getInstantiator()->instantiate($class);
+ foreach (['code', 'message', 'file', 'line'] as $attribute) {
+ if (isset($data[$attribute])) {
+ $property = ReflectionManager::reflectProperty($class, $attribute);
+ $property->setAccessible(true);
+ $property->setValue($exception, $data[$attribute]);
+ }
+ }
+ return $exception;
+ } catch (\ReflectionException $e) {
+ return new \RuntimeException(sprintf(
+ 'Bad data %s: %s',
+ $data['class'],
+ $data['message']
+ ), $data['code']);
+ } catch (\TypeError $e) {
+ return new \RuntimeException(sprintf(
+ 'Uncaught data %s: %s',
+ $data['class'],
+ $data['message']
+ ), $data['code']);
+ }
+ }
+
+ return new \RuntimeException('Bad data data: ' . json_encode($data));
+ }
+
+ public function supportsDenormalization($data, $type, $format = null)
+ {
+ return class_exists($type) && is_a($type, \Throwable::class, true);
+ }
+
+ public function normalize($object, string $format = null, array $context = [])
+ {
+ if ($object instanceof \Serializable) {
+ return serialize($object);
+ }
+ /* @var \Throwable $object */
+ return [
+ 'message' => $object->getMessage(),
+ 'code' => $object->getCode(),
+ 'file' => $object->getFile(),
+ 'line' => $object->getLine(),
+ ];
+ }
+
+ public function supportsNormalization($data, string $format = null)
+ {
+ return $data instanceof \Throwable;
+ }
+
+ public function hasCacheableSupportsMethod(): bool
+ {
+ return \get_class($this) === __CLASS__;
+ }
+
+ protected function getInstantiator(): Instantiator
+ {
+ if ($this->instantiator instanceof Instantiator) {
+ return $this->instantiator;
+ }
+
+ return $this->instantiator = new Instantiator();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Serializer/ScalarNormalizer.php b/server/addons/epay/library/hyperf/utils/src/Serializer/ScalarNormalizer.php
new file mode 100644
index 0000000..5966cc9
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Serializer/ScalarNormalizer.php
@@ -0,0 +1,64 @@
+serialize($obj, 'xml')
+ * $serializer->decode($data, 'xml')
+ * $serializer->denormalize($data, 'Class', 'xml')
+ */
+class Serializer implements Normalizer, SerializerInterface, ContextAwareNormalizerInterface, ContextAwareDenormalizerInterface, ContextAwareEncoderInterface, ContextAwareDecoderInterface
+{
+ private const SCALAR_TYPES = [
+ 'int' => true,
+ 'bool' => true,
+ 'float' => true,
+ 'string' => true,
+ ];
+
+ /**
+ * @var Encoder\ChainEncoder
+ */
+ protected $encoder;
+
+ /**
+ * @var Encoder\ChainDecoder
+ */
+ protected $decoder;
+
+ private $normalizers = [];
+
+ private $denormalizerCache = [];
+
+ private $normalizerCache = [];
+
+ /**
+ * @param (NormalizerInterface|DenormalizerInterface|mixed)[] $normalizers
+ * @param (EncoderInterface|DecoderInterface|mixed)[] $encoders
+ */
+ public function __construct(array $normalizers = [], array $encoders = [])
+ {
+ foreach ($normalizers as $normalizer) {
+ if ($normalizer instanceof SerializerAwareInterface) {
+ $normalizer->setSerializer($this);
+ }
+
+ if ($normalizer instanceof DenormalizerAwareInterface) {
+ $normalizer->setDenormalizer($this);
+ }
+
+ if ($normalizer instanceof NormalizerAwareInterface) {
+ $normalizer->setNormalizer($this);
+ }
+
+ if (! ($normalizer instanceof NormalizerInterface || $normalizer instanceof DenormalizerInterface)) {
+ throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($normalizer), NormalizerInterface::class, DenormalizerInterface::class));
+ }
+ }
+ $this->normalizers = $normalizers;
+
+ $decoders = [];
+ $realEncoders = [];
+ foreach ($encoders as $encoder) {
+ if ($encoder instanceof SerializerAwareInterface) {
+ $encoder->setSerializer($this);
+ }
+ if ($encoder instanceof DecoderInterface) {
+ $decoders[] = $encoder;
+ }
+ if ($encoder instanceof EncoderInterface) {
+ $realEncoders[] = $encoder;
+ }
+
+ if (! ($encoder instanceof EncoderInterface || $encoder instanceof DecoderInterface)) {
+ throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($encoder), EncoderInterface::class, DecoderInterface::class));
+ }
+ }
+ $this->encoder = new ChainEncoder($realEncoders);
+ $this->decoder = new ChainDecoder($decoders);
+ }
+
+ final public function serialize($data, string $format, array $context = []): string
+ {
+ if (! $this->supportsEncoding($format, $context)) {
+ throw new NotEncodableValueException(sprintf('Serialization for the format "%s" is not supported.', $format));
+ }
+
+ if ($this->encoder->needsNormalization($format, $context)) {
+ $data = $this->normalize($data, $format, $context);
+ }
+
+ return $this->encode($data, $format, $context);
+ }
+
+ final public function deserialize($data, string $type, string $format, array $context = [])
+ {
+ if (! $this->supportsDecoding($format, $context)) {
+ throw new NotEncodableValueException(sprintf('Deserialization for the format "%s" is not supported.', $format));
+ }
+
+ $data = $this->decode($data, $format, $context);
+
+ return $this->denormalize($data, $type, $format, $context);
+ }
+
+ public function normalize($data, string $format = null, array $context = [])
+ {
+ // If a normalizer supports the given data, use it
+ if ($normalizer = $this->getNormalizer($data, $format, $context)) {
+ return $normalizer->normalize($data, $format, $context);
+ }
+
+ if ($data === null || is_scalar($data)) {
+ return $data;
+ }
+
+ if (\is_array($data) || $data instanceof \Traversable) {
+ if ($data instanceof \Countable && $data->count() === 0) {
+ return $data;
+ }
+
+ $normalized = [];
+ foreach ($data as $key => $val) {
+ $normalized[$key] = $this->normalize($val, $format, $context);
+ }
+
+ return $normalized;
+ }
+
+ if (\is_object($data)) {
+ if (! $this->normalizers) {
+ throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
+ }
+
+ throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', get_debug_type($data)));
+ }
+
+ throw new NotNormalizableValueException('An unexpected value could not be normalized: ' . (! \is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
+ }
+
+ /**
+ * @param mixed $data
+ * @throws NotNormalizableValueException
+ */
+ public function denormalize($data, string $type, string $format = null, array $context = [])
+ {
+ if (isset(self::SCALAR_TYPES[$type])) {
+ if (is_scalar($data)) {
+ switch ($type) {
+ case 'int':
+ return (int) $data;
+ case 'bool':
+ return (bool) $data;
+ case 'float':
+ return (float) $data;
+ case 'string':
+ return (string) $data;
+ }
+ }
+ }
+
+ if (! $this->normalizers) {
+ throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
+ }
+
+ if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) {
+ return $normalizer->denormalize($data, $type, $format, $context);
+ }
+
+ throw new NotNormalizableValueException(sprintf('Could not denormalize object of type "%s", no supporting normalizer found.', $type));
+ }
+
+ public function supportsNormalization($data, string $format = null, array $context = [])
+ {
+ return $this->getNormalizer($data, $format, $context) !== null;
+ }
+
+ public function supportsDenormalization($data, string $type, string $format = null, array $context = [])
+ {
+ return isset(self::SCALAR_TYPES[$type]) || $this->getDenormalizer($data, $type, $format, $context) !== null;
+ }
+
+ final public function encode($data, string $format, array $context = [])
+ {
+ return $this->encoder->encode($data, $format, $context);
+ }
+
+ final public function decode(string $data, string $format, array $context = [])
+ {
+ return $this->decoder->decode($data, $format, $context);
+ }
+
+ public function supportsEncoding(string $format, array $context = [])
+ {
+ return $this->encoder->supportsEncoding($format, $context);
+ }
+
+ public function supportsDecoding(string $format, array $context = [])
+ {
+ return $this->decoder->supportsDecoding($format, $context);
+ }
+
+ /**
+ * Returns a matching normalizer.
+ *
+ * @param mixed $data Data to get the serializer for
+ * @param string $format Format name, present to give the option to normalizers to act differently based on formats
+ * @param array $context Options available to the normalizer
+ */
+ private function getNormalizer($data, ?string $format, array $context): ?NormalizerInterface
+ {
+ $type = \is_object($data) ? \get_class($data) : 'native-' . \gettype($data);
+
+ if (! isset($this->normalizerCache[$format][$type])) {
+ $this->normalizerCache[$format][$type] = [];
+
+ foreach ($this->normalizers as $k => $normalizer) {
+ if (! $normalizer instanceof NormalizerInterface) {
+ continue;
+ }
+
+ if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) {
+ $this->normalizerCache[$format][$type][$k] = false;
+ } elseif ($normalizer->supportsNormalization($data, $format)) {
+ $this->normalizerCache[$format][$type][$k] = true;
+ break;
+ }
+ }
+ }
+
+ foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
+ $normalizer = $this->normalizers[$k];
+ if ($cached || $normalizer->supportsNormalization($data, $format, $context)) {
+ return $normalizer;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a matching denormalizer.
+ *
+ * @param mixed $data Data to restore
+ * @param string $class The expected class to instantiate
+ * @param string $format Format name, present to give the option to normalizers to act differently based on formats
+ * @param array $context Options available to the denormalizer
+ */
+ private function getDenormalizer($data, string $class, ?string $format, array $context): ?DenormalizerInterface
+ {
+ if (! isset($this->denormalizerCache[$format][$class])) {
+ $this->denormalizerCache[$format][$class] = [];
+
+ foreach ($this->normalizers as $k => $normalizer) {
+ if (! $normalizer instanceof DenormalizerInterface) {
+ continue;
+ }
+
+ if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) {
+ $this->denormalizerCache[$format][$class][$k] = false;
+ } elseif ($normalizer->supportsDenormalization(null, $class, $format)) {
+ $this->denormalizerCache[$format][$class][$k] = true;
+ break;
+ }
+ }
+ }
+
+ foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
+ $normalizer = $this->normalizers[$k];
+ if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) {
+ return $normalizer;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Serializer/SerializerFactory.php b/server/addons/epay/library/hyperf/utils/src/Serializer/SerializerFactory.php
new file mode 100644
index 0000000..0ec9709
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Serializer/SerializerFactory.php
@@ -0,0 +1,39 @@
+serializer = $serializer;
+ }
+
+ public function __invoke()
+ {
+ return new $this->serializer([
+ new ExceptionNormalizer(),
+ new ObjectNormalizer(),
+ new ArrayDenormalizer(),
+ new ScalarNormalizer(),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Serializer/SimpleNormalizer.php b/server/addons/epay/library/hyperf/utils/src/Serializer/SimpleNormalizer.php
new file mode 100644
index 0000000..6b31266
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Serializer/SimpleNormalizer.php
@@ -0,0 +1,40 @@
+serializer = $serializer;
+ }
+
+ public function normalize($object)
+ {
+ return $this->serializer->normalize($object);
+ }
+
+ public function denormalize($data, string $class)
+ {
+ return $this->serializer->denormalize($data, $class);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Str.php b/server/addons/epay/library/hyperf/utils/src/Str.php
new file mode 100644
index 0000000..d23d9a9
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Str.php
@@ -0,0 +1,1320 @@
+ $val) {
+ $value = str_replace($val, (string) $key, $value);
+ }
+
+ return preg_replace('/[^\x20-\x7E]/u', '', $value);
+ }
+
+ /**
+ * Get the portion of a string before a given value.
+ *
+ * @param string $subject
+ * @param string $search
+ * @return string
+ */
+ public static function before($subject, $search)
+ {
+ return $search === '' ? $subject : explode($search, $subject)[0];
+ }
+
+ /**
+ * Get the portion of a string before the last occurrence of a given value.
+ *
+ * @param string $subject
+ * @param string $search
+ * @return string
+ */
+ public static function beforeLast($subject, $search)
+ {
+ if ($search === '') {
+ return $subject;
+ }
+
+ $pos = mb_strrpos($subject, $search);
+
+ if ($pos === false) {
+ return $subject;
+ }
+
+ return static::substr($subject, 0, $pos);
+ }
+
+ /**
+ * Get the portion of a string between two given values.
+ *
+ * @param string $subject
+ * @param string $from
+ * @param string $to
+ * @return string
+ */
+ public static function between($subject, $from, $to)
+ {
+ if ($from === '' || $to === '') {
+ return $subject;
+ }
+
+ return static::beforeLast(static::after($subject, $from), $to);
+ }
+
+ /**
+ * Convert a value to camel case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function camel($value)
+ {
+ if (isset(static::$camelCache[$value])) {
+ return static::$camelCache[$value];
+ }
+
+ return static::$camelCache[$value] = lcfirst(static::studly($value));
+ }
+
+ /**
+ * Determine if a given string contains a given substring.
+ *
+ * @param string $haystack
+ * @param array|string $needles
+ * @return bool
+ */
+ public static function contains($haystack, $needles)
+ {
+ foreach ((array) $needles as $needle) {
+ if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if a given string contains all array values.
+ *
+ * @param string $haystack
+ * @param string[] $needles
+ * @return bool
+ */
+ public static function containsAll($haystack, array $needles)
+ {
+ foreach ($needles as $needle) {
+ if (! static::contains($haystack, $needle)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if a given string ends with a given substring.
+ *
+ * @param string $haystack
+ * @param array|string $needles
+ * @return bool
+ */
+ public static function endsWith($haystack, $needles)
+ {
+ foreach ((array) $needles as $needle) {
+ if (substr($haystack, -strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Cap a string with a single instance of a given value.
+ *
+ * @param string $value
+ * @param string $cap
+ * @return string
+ */
+ public static function finish($value, $cap)
+ {
+ $quoted = preg_quote($cap, '/');
+
+ return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap;
+ }
+
+ /**
+ * Determine if a given string matches a given pattern.
+ *
+ * @param array|string $pattern
+ * @param string $value
+ * @return bool
+ */
+ public static function is($pattern, $value)
+ {
+ $patterns = Arr::wrap($pattern);
+
+ if (empty($patterns)) {
+ return false;
+ }
+
+ foreach ($patterns as $pattern) {
+ // If the given value is an exact match we can of course return true right
+ // from the beginning. Otherwise, we will translate asterisks and do an
+ // actual pattern match against the two strings to see if they match.
+ if ($pattern == $value) {
+ return true;
+ }
+
+ $pattern = preg_quote($pattern, '#');
+
+ // Asterisks are translated into zero-or-more regular expression wildcards
+ // to make it convenient to check if the strings starts with the given
+ // pattern such as "library/*", making any string check convenient.
+ $pattern = str_replace('\*', '.*', $pattern);
+
+ if (preg_match('#^' . $pattern . '\z#u', $value) === 1) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a string to kebab case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function kebab($value)
+ {
+ return static::snake($value, '-');
+ }
+
+ /**
+ * Return the length of the given string.
+ *
+ * @param string $value
+ * @param string $encoding
+ * @return int
+ */
+ public static function length($value, $encoding = null)
+ {
+ if ($encoding) {
+ return mb_strlen($value, $encoding);
+ }
+
+ return mb_strlen($value);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ *
+ * @param string $value
+ * @param int $limit
+ * @param string $end
+ * @return string
+ */
+ public static function limit($value, $limit = 100, $end = '...')
+ {
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
+ return $value;
+ }
+
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end;
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function lower($value)
+ {
+ return mb_strtolower($value, 'UTF-8');
+ }
+
+ /**
+ * Limit the number of words in a string.
+ */
+ public static function words(string $value, int $words = 100, string $end = '...'): string
+ {
+ preg_match('/^\s*+(?:\S++\s*+){1,' . $words . '}/u', $value, $matches);
+
+ if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+ return $value;
+ }
+
+ return rtrim($matches[0]) . $end;
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @param string $subject
+ * @return string
+ */
+ public static function match($pattern, $subject)
+ {
+ preg_match($pattern, $subject, $matches);
+
+ if (! $matches) {
+ return '';
+ }
+
+ return $matches[1] ?? $matches[0];
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @param string $subject
+ * @return Collection
+ */
+ public static function matchAll($pattern, $subject)
+ {
+ preg_match_all($pattern, $subject, $matches);
+
+ if (empty($matches[0])) {
+ return collect();
+ }
+
+ return collect($matches[1] ?? $matches[0]);
+ }
+
+ /**
+ * Pad both sides of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padBoth($value, $length, $pad = ' ')
+ {
+ return str_pad($value, $length, $pad, STR_PAD_BOTH);
+ }
+
+ /**
+ * Pad the left side of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padLeft($value, $length, $pad = ' ')
+ {
+ return str_pad($value, $length, $pad, STR_PAD_LEFT);
+ }
+
+ /**
+ * Pad the right side of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padRight($value, $length, $pad = ' ')
+ {
+ return str_pad($value, $length, $pad, STR_PAD_RIGHT);
+ }
+
+ /**
+ * Parse a Class@method style callback into class and method.
+ *
+ * @param null|string $default
+ */
+ public static function parseCallback(string $callback, $default = null): array
+ {
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+ }
+
+ /**
+ * Pluralize the last word of an English, studly caps case string.
+ */
+ public static function pluralStudly(string $value, int $count = 2): string
+ {
+ $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ $lastWord = array_pop($parts);
+
+ return implode('', $parts) . self::plural($lastWord, $count);
+ }
+
+ /**
+ * Get the plural form of an English word.
+ */
+ public static function plural(string $value, int $count = 2): string
+ {
+ return Pluralizer::plural($value, $count);
+ }
+
+ /**
+ * Generate a more truly "random" alpha-numeric string.
+ */
+ public static function random(int $length = 16): string
+ {
+ $string = '';
+
+ while (($len = strlen($string)) < $length) {
+ $size = $length - $len;
+
+ $bytes = random_bytes($size);
+
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Repeat the given string.
+ *
+ * @return string
+ */
+ public static function repeat(string $string, int $times)
+ {
+ return str_repeat($string, $times);
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ */
+ public static function replaceArray(string $search, array $replace, string $subject): string
+ {
+ foreach ($replace as $value) {
+ $subject = static::replaceFirst($search, (string) $value, $subject);
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the given value in the given string.
+ *
+ * @param string|string[] $search
+ * @param string|string[] $replace
+ * @param string|string[] $subject
+ * @return string
+ */
+ public static function replace($search, $replace, $subject)
+ {
+ return str_replace($search, $replace, $subject);
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ */
+ public static function replaceFirst(string $search, string $replace, string $subject): string
+ {
+ if ($search == '') {
+ return $subject;
+ }
+
+ $position = strpos($subject, $search);
+
+ if ($position !== false) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ */
+ public static function replaceLast(string $search, string $replace, string $subject): string
+ {
+ $position = strrpos($subject, $search);
+
+ if ($position !== false) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Remove any occurrence of the given string in the subject.
+ *
+ * @param array|string $search
+ * @param string $subject
+ * @param bool $caseSensitive
+ * @return string
+ */
+ public static function remove($search, $subject, $caseSensitive = true)
+ {
+ return $caseSensitive
+ ? str_replace($search, '', $subject)
+ : str_ireplace($search, '', $subject);
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ */
+ public static function start(string $value, string $prefix): string
+ {
+ $quoted = preg_quote($prefix, '/');
+
+ return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value);
+ }
+
+ /**
+ * Strip HTML and PHP tags from the given string.
+ *
+ * @param null|string|string[] $allowedTags
+ */
+ public static function stripTags(string $value, $allowedTags = null): string
+ {
+ return strip_tags($value, $allowedTags);
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ */
+ public static function upper(string $value): string
+ {
+ return mb_strtoupper($value, 'UTF-8');
+ }
+
+ /**
+ * Convert the given string to title case.
+ */
+ public static function title(string $value): string
+ {
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ /**
+ * Get the singular form of an English word.
+ */
+ public static function singular(string $value): string
+ {
+ return Pluralizer::singular($value);
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ */
+ public static function slug(string $title, string $separator = '-', string $language = 'en'): string
+ {
+ $title = $language ? static::ascii($title, $language) : $title;
+
+ // Convert all dashes/underscores into separator
+ $flip = $separator === '-' ? '_' : '-';
+
+ $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title);
+
+ // Replace @ with the word 'at'
+ $title = str_replace('@', $separator . 'at' . $separator, $title);
+
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
+ $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', mb_strtolower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
+
+ return trim($title, $separator);
+ }
+
+ /**
+ * Convert a string to snake case.
+ */
+ public static function snake(string $value, string $delimiter = '_'): string
+ {
+ $key = $value;
+
+ if (isset(static::$snakeCache[$key][$delimiter])) {
+ return static::$snakeCache[$key][$delimiter];
+ }
+
+ if (! ctype_lower($value)) {
+ $value = preg_replace('/\s+/u', '', ucwords($value));
+
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
+ }
+
+ return static::$snakeCache[$key][$delimiter] = $value;
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param array|string $needles
+ */
+ public static function startsWith(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if ($needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ */
+ public static function studly(string $value, string $gap = ''): string
+ {
+ $key = $value;
+
+ if (isset(static::$studlyCache[$key])) {
+ return static::$studlyCache[$key];
+ }
+
+ $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+ return static::$studlyCache[$key] = str_replace(' ', $gap, $value);
+ }
+
+ /**
+ * Returns the portion of string specified by the start and length parameters.
+ *
+ * @param string $string
+ * @param int $start
+ * @param null|int $length
+ * @return string
+ */
+ public static function substr($string, $start, $length = null)
+ {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ /**
+ * Returns the number of substring occurrences.
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @param int $offset
+ * @param null|int $length
+ * @return int
+ */
+ public static function substrCount($haystack, $needle, $offset = 0, $length = null)
+ {
+ if (! is_null($length)) {
+ return substr_count($haystack, $needle, $offset, $length);
+ }
+ return substr_count($haystack, $needle, $offset);
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ */
+ public static function ucfirst(string $string): string
+ {
+ return static::upper(static::substr($string, 0, 1)) . static::substr($string, 1);
+ }
+
+ /**
+ * Replaces the first or the last ones chars from a string by a given char.
+ *
+ * @param int $offset if is negative it starts from the end
+ * @param string $replacement default is *
+ */
+ public static function mask(string $string, int $offset = 0, int $length = 0, string $replacement = '*')
+ {
+ if ($length < 0) {
+ throw new InvalidArgumentException('The length must equal or greater than zero.');
+ }
+
+ $stringLength = mb_strlen($string);
+ $absOffset = abs($offset);
+ if ($absOffset >= $stringLength) {
+ return $string;
+ }
+
+ $hiddenLength = $length ?: $stringLength - $absOffset;
+
+ if ($offset >= 0) {
+ return mb_substr($string, 0, $offset) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset + $hiddenLength);
+ }
+
+ return mb_substr($string, 0, max($stringLength - $hiddenLength - $absOffset, 0)) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset);
+ }
+
+ /**
+ * Returns the replacements for the ascii method.
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function charsArray(): array
+ {
+ static $charsArray;
+
+ if (isset($charsArray)) {
+ return $charsArray;
+ }
+
+ return $charsArray = [
+ '0' => ['°', '₀', '۰', '0'],
+ '1' => ['¹', '₁', '۱', '1'],
+ '2' => ['²', '₂', '۲', '2'],
+ '3' => ['³', '₃', '۳', '3'],
+ '4' => ['⁴', '₄', '۴', '٤', '4'],
+ '5' => ['⁵', '₅', '۵', '٥', '5'],
+ '6' => ['⁶', '₆', '۶', '٦', '6'],
+ '7' => ['⁷', '₇', '۷', '7'],
+ '8' => ['⁸', '₈', '۸', '8'],
+ '9' => ['⁹', '₉', '۹', '9'],
+ 'a' => [
+ 'à',
+ 'á',
+ 'ả',
+ 'ã',
+ 'ạ',
+ 'ă',
+ 'ắ',
+ 'ằ',
+ 'ẳ',
+ 'ẵ',
+ 'ặ',
+ 'â',
+ 'ấ',
+ 'ầ',
+ 'ẩ',
+ 'ẫ',
+ 'ậ',
+ 'ā',
+ 'ą',
+ 'å',
+ 'α',
+ 'ά',
+ 'ἀ',
+ 'ἁ',
+ 'ἂ',
+ 'ἃ',
+ 'ἄ',
+ 'ἅ',
+ 'ἆ',
+ 'ἇ',
+ 'ᾀ',
+ 'ᾁ',
+ 'ᾂ',
+ 'ᾃ',
+ 'ᾄ',
+ 'ᾅ',
+ 'ᾆ',
+ 'ᾇ',
+ 'ὰ',
+ 'ά',
+ 'ᾰ',
+ 'ᾱ',
+ 'ᾲ',
+ 'ᾳ',
+ 'ᾴ',
+ 'ᾶ',
+ 'ᾷ',
+ 'а',
+ 'أ',
+ 'အ',
+ 'ာ',
+ 'ါ',
+ 'ǻ',
+ 'ǎ',
+ 'ª',
+ 'ა',
+ 'अ',
+ 'ا',
+ 'a',
+ 'ä',
+ ],
+ 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
+ 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
+ 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
+ 'e' => [
+ 'é',
+ 'è',
+ 'ẻ',
+ 'ẽ',
+ 'ẹ',
+ 'ê',
+ 'ế',
+ 'ề',
+ 'ể',
+ 'ễ',
+ 'ệ',
+ 'ë',
+ 'ē',
+ 'ę',
+ 'ě',
+ 'ĕ',
+ 'ė',
+ 'ε',
+ 'έ',
+ 'ἐ',
+ 'ἑ',
+ 'ἒ',
+ 'ἓ',
+ 'ἔ',
+ 'ἕ',
+ 'ὲ',
+ 'έ',
+ 'е',
+ 'ё',
+ 'э',
+ 'є',
+ 'ə',
+ 'ဧ',
+ 'ေ',
+ 'ဲ',
+ 'ე',
+ 'ए',
+ 'إ',
+ 'ئ',
+ 'e',
+ ],
+ 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
+ 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
+ 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
+ 'i' => [
+ 'í',
+ 'ì',
+ 'ỉ',
+ 'ĩ',
+ 'ị',
+ 'î',
+ 'ï',
+ 'ī',
+ 'ĭ',
+ 'į',
+ 'ı',
+ 'ι',
+ 'ί',
+ 'ϊ',
+ 'ΐ',
+ 'ἰ',
+ 'ἱ',
+ 'ἲ',
+ 'ἳ',
+ 'ἴ',
+ 'ἵ',
+ 'ἶ',
+ 'ἷ',
+ 'ὶ',
+ 'ί',
+ 'ῐ',
+ 'ῑ',
+ 'ῒ',
+ 'ΐ',
+ 'ῖ',
+ 'ῗ',
+ 'і',
+ 'ї',
+ 'и',
+ 'ဣ',
+ 'ိ',
+ 'ီ',
+ 'ည်',
+ 'ǐ',
+ 'ი',
+ 'इ',
+ 'ی',
+ 'i',
+ ],
+ 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
+ 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
+ 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
+ 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
+ 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
+ 'o' => [
+ 'ó',
+ 'ò',
+ 'ỏ',
+ 'õ',
+ 'ọ',
+ 'ô',
+ 'ố',
+ 'ồ',
+ 'ổ',
+ 'ỗ',
+ 'ộ',
+ 'ơ',
+ 'ớ',
+ 'ờ',
+ 'ở',
+ 'ỡ',
+ 'ợ',
+ 'ø',
+ 'ō',
+ 'ő',
+ 'ŏ',
+ 'ο',
+ 'ὀ',
+ 'ὁ',
+ 'ὂ',
+ 'ὃ',
+ 'ὄ',
+ 'ὅ',
+ 'ὸ',
+ 'ό',
+ 'о',
+ 'و',
+ 'θ',
+ 'ို',
+ 'ǒ',
+ 'ǿ',
+ 'º',
+ 'ო',
+ 'ओ',
+ 'o',
+ 'ö',
+ ],
+ 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
+ 'q' => ['ყ', 'q'],
+ 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
+ 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
+ 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
+ 'u' => [
+ 'ú',
+ 'ù',
+ 'ủ',
+ 'ũ',
+ 'ụ',
+ 'ư',
+ 'ứ',
+ 'ừ',
+ 'ử',
+ 'ữ',
+ 'ự',
+ 'û',
+ 'ū',
+ 'ů',
+ 'ű',
+ 'ŭ',
+ 'ų',
+ 'µ',
+ 'у',
+ 'ဉ',
+ 'ု',
+ 'ူ',
+ 'ǔ',
+ 'ǖ',
+ 'ǘ',
+ 'ǚ',
+ 'ǜ',
+ 'უ',
+ 'उ',
+ 'u',
+ 'ў',
+ 'ü',
+ ],
+ 'v' => ['в', 'ვ', 'ϐ', 'v'],
+ 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
+ 'x' => ['χ', 'ξ', 'x'],
+ 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
+ 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
+ 'aa' => ['ع', 'आ', 'آ'],
+ 'ae' => ['æ', 'ǽ'],
+ 'ai' => ['ऐ'],
+ 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
+ 'dj' => ['ђ', 'đ'],
+ 'dz' => ['џ', 'ძ'],
+ 'ei' => ['ऍ'],
+ 'gh' => ['غ', 'ღ'],
+ 'ii' => ['ई'],
+ 'ij' => ['ij'],
+ 'kh' => ['х', 'خ', 'ხ'],
+ 'lj' => ['љ'],
+ 'nj' => ['њ'],
+ 'oe' => ['ö', 'œ', 'ؤ'],
+ 'oi' => ['ऑ'],
+ 'oii' => ['ऒ'],
+ 'ps' => ['ψ'],
+ 'sh' => ['ш', 'შ', 'ش'],
+ 'shch' => ['щ'],
+ 'ss' => ['ß'],
+ 'sx' => ['ŝ'],
+ 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
+ 'ts' => ['ц', 'ც', 'წ'],
+ 'ue' => ['ü'],
+ 'uu' => ['ऊ'],
+ 'ya' => ['я'],
+ 'yu' => ['ю'],
+ 'zh' => ['ж', 'ჟ', 'ژ'],
+ '(c)' => ['©'],
+ 'A' => [
+ 'Á',
+ 'À',
+ 'Ả',
+ 'Ã',
+ 'Ạ',
+ 'Ă',
+ 'Ắ',
+ 'Ằ',
+ 'Ẳ',
+ 'Ẵ',
+ 'Ặ',
+ 'Â',
+ 'Ấ',
+ 'Ầ',
+ 'Ẩ',
+ 'Ẫ',
+ 'Ậ',
+ 'Å',
+ 'Ā',
+ 'Ą',
+ 'Α',
+ 'Ά',
+ 'Ἀ',
+ 'Ἁ',
+ 'Ἂ',
+ 'Ἃ',
+ 'Ἄ',
+ 'Ἅ',
+ 'Ἆ',
+ 'Ἇ',
+ 'ᾈ',
+ 'ᾉ',
+ 'ᾊ',
+ 'ᾋ',
+ 'ᾌ',
+ 'ᾍ',
+ 'ᾎ',
+ 'ᾏ',
+ 'Ᾰ',
+ 'Ᾱ',
+ 'Ὰ',
+ 'Ά',
+ 'ᾼ',
+ 'А',
+ 'Ǻ',
+ 'Ǎ',
+ 'A',
+ 'Ä',
+ ],
+ 'B' => ['Б', 'Β', 'ब', 'B'],
+ 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
+ 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
+ 'E' => [
+ 'É',
+ 'È',
+ 'Ẻ',
+ 'Ẽ',
+ 'Ẹ',
+ 'Ê',
+ 'Ế',
+ 'Ề',
+ 'Ể',
+ 'Ễ',
+ 'Ệ',
+ 'Ë',
+ 'Ē',
+ 'Ę',
+ 'Ě',
+ 'Ĕ',
+ 'Ė',
+ 'Ε',
+ 'Έ',
+ 'Ἐ',
+ 'Ἑ',
+ 'Ἒ',
+ 'Ἓ',
+ 'Ἔ',
+ 'Ἕ',
+ 'Έ',
+ 'Ὲ',
+ 'Е',
+ 'Ё',
+ 'Э',
+ 'Є',
+ 'Ə',
+ 'E',
+ ],
+ 'F' => ['Ф', 'Φ', 'F'],
+ 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
+ 'H' => ['Η', 'Ή', 'Ħ', 'H'],
+ 'I' => [
+ 'Í',
+ 'Ì',
+ 'Ỉ',
+ 'Ĩ',
+ 'Ị',
+ 'Î',
+ 'Ï',
+ 'Ī',
+ 'Ĭ',
+ 'Į',
+ 'İ',
+ 'Ι',
+ 'Ί',
+ 'Ϊ',
+ 'Ἰ',
+ 'Ἱ',
+ 'Ἳ',
+ 'Ἴ',
+ 'Ἵ',
+ 'Ἶ',
+ 'Ἷ',
+ 'Ῐ',
+ 'Ῑ',
+ 'Ὶ',
+ 'Ί',
+ 'И',
+ 'І',
+ 'Ї',
+ 'Ǐ',
+ 'ϒ',
+ 'I',
+ ],
+ 'J' => ['J'],
+ 'K' => ['К', 'Κ', 'K'],
+ 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
+ 'M' => ['М', 'Μ', 'M'],
+ 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
+ 'O' => [
+ 'Ó',
+ 'Ò',
+ 'Ỏ',
+ 'Õ',
+ 'Ọ',
+ 'Ô',
+ 'Ố',
+ 'Ồ',
+ 'Ổ',
+ 'Ỗ',
+ 'Ộ',
+ 'Ơ',
+ 'Ớ',
+ 'Ờ',
+ 'Ở',
+ 'Ỡ',
+ 'Ợ',
+ 'Ø',
+ 'Ō',
+ 'Ő',
+ 'Ŏ',
+ 'Ο',
+ 'Ό',
+ 'Ὀ',
+ 'Ὁ',
+ 'Ὂ',
+ 'Ὃ',
+ 'Ὄ',
+ 'Ὅ',
+ 'Ὸ',
+ 'Ό',
+ 'О',
+ 'Θ',
+ 'Ө',
+ 'Ǒ',
+ 'Ǿ',
+ 'O',
+ 'Ö',
+ ],
+ 'P' => ['П', 'Π', 'P'],
+ 'Q' => ['Q'],
+ 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
+ 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
+ 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
+ 'U' => [
+ 'Ú',
+ 'Ù',
+ 'Ủ',
+ 'Ũ',
+ 'Ụ',
+ 'Ư',
+ 'Ứ',
+ 'Ừ',
+ 'Ử',
+ 'Ữ',
+ 'Ự',
+ 'Û',
+ 'Ū',
+ 'Ů',
+ 'Ű',
+ 'Ŭ',
+ 'Ų',
+ 'У',
+ 'Ǔ',
+ 'Ǖ',
+ 'Ǘ',
+ 'Ǚ',
+ 'Ǜ',
+ 'U',
+ 'Ў',
+ 'Ü',
+ ],
+ 'V' => ['В', 'V'],
+ 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
+ 'X' => ['Χ', 'Ξ', 'X'],
+ 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
+ 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
+ 'AE' => ['Æ', 'Ǽ'],
+ 'Ch' => ['Ч'],
+ 'Dj' => ['Ђ'],
+ 'Dz' => ['Џ'],
+ 'Gx' => ['Ĝ'],
+ 'Hx' => ['Ĥ'],
+ 'Ij' => ['IJ'],
+ 'Jx' => ['Ĵ'],
+ 'Kh' => ['Х'],
+ 'Lj' => ['Љ'],
+ 'Nj' => ['Њ'],
+ 'Oe' => ['Œ'],
+ 'Ps' => ['Ψ'],
+ 'Sh' => ['Ш'],
+ 'Shch' => ['Щ'],
+ 'Ss' => ['ẞ'],
+ 'Th' => ['Þ'],
+ 'Ts' => ['Ц'],
+ 'Ya' => ['Я'],
+ 'Yu' => ['Ю'],
+ 'Zh' => ['Ж'],
+ ' ' => [
+ "\xC2\xA0",
+ "\xE2\x80\x80",
+ "\xE2\x80\x81",
+ "\xE2\x80\x82",
+ "\xE2\x80\x83",
+ "\xE2\x80\x84",
+ "\xE2\x80\x85",
+ "\xE2\x80\x86",
+ "\xE2\x80\x87",
+ "\xE2\x80\x88",
+ "\xE2\x80\x89",
+ "\xE2\x80\x8A",
+ "\xE2\x80\xAF",
+ "\xE2\x81\x9F",
+ "\xE3\x80\x80",
+ "\xEF\xBE\xA0",
+ ],
+ ];
+ }
+
+ /**
+ * Returns the language specific replacements for the ascii method.
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ * @return null|array
+ */
+ protected static function languageSpecificCharsArray(string $language)
+ {
+ static $languageSpecific;
+
+ if (! isset($languageSpecific)) {
+ $languageSpecific = [
+ 'bg' => [
+ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
+ ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
+ ],
+ 'de' => [
+ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
+ ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
+ ],
+ ];
+ }
+
+ return $languageSpecific[$language] ?? null;
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Stringable.php b/server/addons/epay/library/hyperf/utils/src/Stringable.php
new file mode 100644
index 0000000..e0f7428
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Stringable.php
@@ -0,0 +1,803 @@
+value = (string) $value;
+ }
+
+ /**
+ * Proxy dynamic properties onto methods.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->{$key}();
+ }
+
+ /**
+ * Get the raw string value.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->value;
+ }
+
+ /**
+ * Return the remainder of a string after the first occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function after($search)
+ {
+ return new static(Str::after($this->value, $search));
+ }
+
+ /**
+ * Return the remainder of a string after the last occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function afterLast($search)
+ {
+ return new static(Str::afterLast($this->value, $search));
+ }
+
+ /**
+ * Append the given values to the string.
+ *
+ * @param array $values
+ * @return static
+ */
+ public function append(...$values)
+ {
+ return new static($this->value . implode('', $values));
+ }
+
+ /**
+ * Transliterate a UTF-8 value to ASCII.
+ *
+ * @param string $language
+ * @return static
+ */
+ public function ascii($language = 'en')
+ {
+ return new static(Str::ascii($this->value, $language));
+ }
+
+ /**
+ * Get the trailing name component of the path.
+ *
+ * @param string $suffix
+ * @return static
+ */
+ public function basename($suffix = '')
+ {
+ return new static(basename($this->value, $suffix));
+ }
+
+ /**
+ * Get the basename of the class path.
+ *
+ * @return static
+ */
+ public function classBasename()
+ {
+ return new static(class_basename($this->value));
+ }
+
+ /**
+ * Get the portion of a string before the first occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function before($search)
+ {
+ return new static(Str::before($this->value, $search));
+ }
+
+ /**
+ * Get the portion of a string before the last occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function beforeLast($search)
+ {
+ return new static(Str::beforeLast($this->value, $search));
+ }
+
+ /**
+ * Get the portion of a string between two given values.
+ *
+ * @param string $from
+ * @param string $to
+ * @return static
+ */
+ public function between($from, $to)
+ {
+ return new static(Str::between($this->value, $from, $to));
+ }
+
+ /**
+ * Convert a value to camel case.
+ *
+ * @return static
+ */
+ public function camel()
+ {
+ return new static(Str::camel($this->value));
+ }
+
+ /**
+ * Determine if a given string contains a given substring.
+ *
+ * @param array|string $needles
+ * @return bool
+ */
+ public function contains($needles)
+ {
+ return Str::contains($this->value, $needles);
+ }
+
+ /**
+ * Determine if a given string contains all array values.
+ *
+ * @return bool
+ */
+ public function containsAll(array $needles)
+ {
+ return Str::containsAll($this->value, $needles);
+ }
+
+ /**
+ * Get the parent directory's path.
+ *
+ * @param int $levels
+ * @return static
+ */
+ public function dirname($levels = 1)
+ {
+ return new static(dirname($this->value, $levels));
+ }
+
+ /**
+ * Determine if a given string ends with a given substring.
+ *
+ * @param array|string $needles
+ * @return bool
+ */
+ public function endsWith($needles)
+ {
+ return Str::endsWith($this->value, $needles);
+ }
+
+ /**
+ * Determine if the string is an exact match with the given value.
+ *
+ * @param string $value
+ * @return bool
+ */
+ public function exactly($value)
+ {
+ return $this->value === $value;
+ }
+
+ /**
+ * Explode the string into an array.
+ *
+ * @param string $delimiter
+ * @param int $limit
+ * @return \Hyperf\Utils\Collection
+ */
+ public function explode($delimiter, $limit = PHP_INT_MAX)
+ {
+ return collect(explode($delimiter, $this->value, $limit));
+ }
+
+ /**
+ * Split a string using a regular expression or by length.
+ *
+ * @param int|string $pattern
+ * @param int $limit
+ * @param int $flags
+ * @return \Hyperf\Utils\Collection
+ */
+ public function split($pattern, $limit = -1, $flags = 0)
+ {
+ if (filter_var($pattern, FILTER_VALIDATE_INT) !== false) {
+ return collect(mb_str_split($this->value, $pattern));
+ }
+
+ $segments = preg_split($pattern, $this->value, $limit, $flags);
+
+ return ! empty($segments) ? collect($segments) : collect();
+ }
+
+ /**
+ * Cap a string with a single instance of a given value.
+ *
+ * @param string $cap
+ * @return static
+ */
+ public function finish($cap)
+ {
+ return new static(Str::finish($this->value, $cap));
+ }
+
+ /**
+ * Determine if a given string matches a given pattern.
+ *
+ * @param array|string $pattern
+ * @return bool
+ */
+ public function is($pattern)
+ {
+ return Str::is($pattern, $this->value);
+ }
+
+ /**
+ * Determine if the given string is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->value === '';
+ }
+
+ /**
+ * Determine if the given string is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * Convert a string to kebab case.
+ *
+ * @return static
+ */
+ public function kebab()
+ {
+ return new static(Str::kebab($this->value));
+ }
+
+ /**
+ * Return the length of the given string.
+ *
+ * @param string $encoding
+ * @return int
+ */
+ public function length($encoding = null)
+ {
+ return Str::length($this->value, $encoding);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ *
+ * @param int $limit
+ * @param string $end
+ * @return static
+ */
+ public function limit($limit = 100, $end = '...')
+ {
+ return new static(Str::limit($this->value, $limit, $end));
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ *
+ * @return static
+ */
+ public function lower()
+ {
+ return new static(Str::lower($this->value));
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @return static
+ */
+ public function match($pattern)
+ {
+ preg_match($pattern, $this->value, $matches);
+
+ if (! $matches) {
+ return new static();
+ }
+
+ return new static($matches[1] ?? $matches[0]);
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @return \Hyperf\Utils\Collection
+ */
+ public function matchAll($pattern)
+ {
+ preg_match_all($pattern, $this->value, $matches);
+
+ if (empty($matches[0])) {
+ return collect();
+ }
+
+ return collect($matches[1] ?? $matches[0]);
+ }
+
+ /**
+ * Determine if the string matches the given pattern.
+ *
+ * @param string $pattern
+ * @return bool
+ */
+ public function test($pattern)
+ {
+ return $this->match($pattern)->isNotEmpty();
+ }
+
+ /**
+ * Pad both sides of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padBoth($length, $pad = ' ')
+ {
+ return new static(Str::padBoth($this->value, $length, $pad));
+ }
+
+ /**
+ * Pad the left side of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padLeft($length, $pad = ' ')
+ {
+ return new static(Str::padLeft($this->value, $length, $pad));
+ }
+
+ /**
+ * Pad the right side of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padRight($length, $pad = ' ')
+ {
+ return new static(Str::padRight($this->value, $length, $pad));
+ }
+
+ /**
+ * Parse a Class@method style callback into class and method.
+ *
+ * @param null|string $default
+ * @return array
+ */
+ public function parseCallback($default = null)
+ {
+ return Str::parseCallback($this->value, $default);
+ }
+
+ /**
+ * Call the given callback and return a new string.
+ *
+ * @return static
+ */
+ public function pipe(callable $callback)
+ {
+ return new static(call_user_func($callback, $this));
+ }
+
+ /**
+ * Get the plural form of an English word.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function plural($count = 2)
+ {
+ return new static(Str::plural($this->value, $count));
+ }
+
+ /**
+ * Pluralize the last word of an English, studly caps case string.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function pluralStudly($count = 2)
+ {
+ return new static(Str::pluralStudly($this->value, $count));
+ }
+
+ /**
+ * Prepend the given values to the string.
+ *
+ * @param array $values
+ * @return static
+ */
+ public function prepend(...$values)
+ {
+ return new static(implode('', $values) . $this->value);
+ }
+
+ /**
+ * Remove any occurrence of the given string in the subject.
+ *
+ * @param array|string $search
+ * @param bool $caseSensitive
+ * @return static
+ */
+ public function remove($search, $caseSensitive = true)
+ {
+ return new static(Str::remove($search, $this->value, $caseSensitive));
+ }
+
+ /**
+ * Repeat the string.
+ *
+ * @return static
+ */
+ public function repeat(int $times)
+ {
+ return new static(Str::repeat($this->value, $times));
+ }
+
+ /**
+ * Replace the given value in the given string.
+ *
+ * @param string|string[] $search
+ * @param string|string[] $replace
+ * @return static
+ */
+ public function replace($search, $replace)
+ {
+ return new static(Str::replace($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function replaceArray($search, array $replace)
+ {
+ return new static(Str::replaceArray($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @return static
+ */
+ public function replaceFirst($search, $replace)
+ {
+ return new static(Str::replaceFirst($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @return static
+ */
+ public function replaceLast($search, $replace)
+ {
+ return new static(Str::replaceLast($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the patterns matching the given regular expression.
+ *
+ * @param string $pattern
+ * @param \Closure|string $replace
+ * @param int $limit
+ * @return static
+ */
+ public function replaceMatches($pattern, $replace, $limit = -1)
+ {
+ if ($replace instanceof Closure) {
+ return new static(preg_replace_callback($pattern, $replace, $this->value, $limit));
+ }
+
+ return new static(preg_replace($pattern, $replace, $this->value, $limit));
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ *
+ * @param string $prefix
+ * @return static
+ */
+ public function start($prefix)
+ {
+ return new static(Str::start($this->value, $prefix));
+ }
+
+ /**
+ * Strip HTML and PHP tags from the given string.
+ *
+ * @param null|string|string[] $allowedTags
+ * @return static
+ */
+ public function stripTags($allowedTags = null)
+ {
+ return new static(strip_tags($this->value, $allowedTags));
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ *
+ * @return static
+ */
+ public function upper()
+ {
+ return new static(Str::upper($this->value));
+ }
+
+ /**
+ * Convert the given string to title case.
+ *
+ * @return static
+ */
+ public function title()
+ {
+ return new static(Str::title($this->value));
+ }
+
+ /**
+ * Get the singular form of an English word.
+ *
+ * @return static
+ */
+ public function singular()
+ {
+ return new static(Str::singular($this->value));
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ *
+ * @param string $separator
+ * @param null|string $language
+ * @return static
+ */
+ public function slug($separator = '-', $language = 'en')
+ {
+ return new static(Str::slug($this->value, $separator, $language));
+ }
+
+ /**
+ * Convert a string to snake case.
+ *
+ * @param string $delimiter
+ * @return static
+ */
+ public function snake($delimiter = '_')
+ {
+ return new static(Str::snake($this->value, $delimiter));
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param array|string $needles
+ * @return bool
+ */
+ public function startsWith($needles)
+ {
+ return Str::startsWith($this->value, $needles);
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ *
+ * @return static
+ */
+ public function studly()
+ {
+ return new static(Str::studly($this->value));
+ }
+
+ /**
+ * Returns the portion of the string specified by the start and length parameters.
+ *
+ * @param int $start
+ * @param null|int $length
+ * @return static
+ */
+ public function substr($start, $length = null)
+ {
+ return new static(Str::substr($this->value, $start, $length));
+ }
+
+ /**
+ * Returns the number of substring occurrences.
+ *
+ * @param string $needle
+ * @param null|int $offset
+ * @param null|int $length
+ * @return int
+ */
+ public function substrCount($needle, $offset = null, $length = null)
+ {
+ return Str::substrCount($this->value, $needle, $offset ?? 0, $length);
+ }
+
+ /**
+ * Trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function trim($characters = null)
+ {
+ return new static(trim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Left trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function ltrim($characters = null)
+ {
+ return new static(ltrim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Right trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function rtrim($characters = null)
+ {
+ return new static(rtrim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ *
+ * @return static
+ */
+ public function ucfirst()
+ {
+ return new static(Str::ucfirst($this->value));
+ }
+
+ /**
+ * Replaces the first or the last ones chars from a string by a given char.
+ *
+ * @param int $offset if is negative it starts from the end
+ * @param string $replacement default is *
+ * @return static
+ */
+ public function mask(int $offset = 0, int $length = 0, string $replacement = '*')
+ {
+ return new static(Str::mask($this->value, $offset, $length, $replacement));
+ }
+
+ /**
+ * Execute the given callback if the string is empty.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function whenEmpty($callback)
+ {
+ if ($this->isEmpty()) {
+ $result = $callback($this);
+
+ return is_null($result) ? $this : $result;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Execute the given callback if the string is not empty.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function whenNotEmpty($callback)
+ {
+ if ($this->isNotEmpty()) {
+ $result = $callback($this);
+
+ return is_null($result) ? $this : $result;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Limit the number of words in a string.
+ *
+ * @param int $words
+ * @param string $end
+ * @return static
+ */
+ public function words($words = 100, $end = '...')
+ {
+ return new static(Str::words($this->value, $words, $end));
+ }
+
+ /**
+ * Get the number of words a string contains.
+ *
+ * @return int
+ */
+ public function wordCount()
+ {
+ return str_word_count($this->value);
+ }
+
+ /**
+ * Convert the object to a string when JSON encoded.
+ *
+ * @return string
+ */
+ public function jsonSerialize()
+ {
+ return $this->__toString();
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Traits/Conditionable.php b/server/addons/epay/library/hyperf/utils/src/Traits/Conditionable.php
new file mode 100644
index 0000000..f21fd1c
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Traits/Conditionable.php
@@ -0,0 +1,57 @@
+getTargetObject();
+ return $target->{$name}(...$arguments);
+ }
+
+ public function __get($name)
+ {
+ $target = $this->getTargetObject();
+ return $target->{$name};
+ }
+
+ public function __set($name, $value)
+ {
+ $target = $this->getTargetObject();
+ return $target->{$name} = $value;
+ }
+
+ protected function getTargetObject()
+ {
+ if (! isset($this->proxyKey)) {
+ throw new \RuntimeException('$proxyKey property of class missing.');
+ }
+ return Context::get($this->proxyKey);
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Traits/ForwardsCalls.php b/server/addons/epay/library/hyperf/utils/src/Traits/ForwardsCalls.php
new file mode 100644
index 0000000..aa3d080
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Traits/ForwardsCalls.php
@@ -0,0 +1,53 @@
+{$method}(...$parameters);
+ } catch (Error|BadMethodCallException $e) {
+ $pattern = '~^Call to undefined method (?P[^:]+)::(?P[^\(]+)\(\)$~';
+
+ if (! preg_match($pattern, $e->getMessage(), $matches)) {
+ throw $e;
+ }
+
+ if ($matches['class'] !== get_class($object) || $matches['method'] !== $method) {
+ throw $e;
+ }
+
+ static::throwBadMethodCallException($method);
+ }
+ }
+
+ /**
+ * Throw a bad method call exception for the given method.
+ * @throws BadMethodCallException
+ */
+ protected static function throwBadMethodCallException(string $method): void
+ {
+ throw new BadMethodCallException(sprintf('Call to undefined method %s::%s()', static::class, $method));
+ }
+}
diff --git a/server/addons/epay/library/hyperf/utils/src/Traits/Macroable.php b/server/addons/epay/library/hyperf/utils/src/Traits/Macroable.php
new file mode 100644
index 0000000..815e027
--- /dev/null
+++ b/server/addons/epay/library/hyperf/utils/src/Traits/Macroable.php
@@ -0,0 +1,20 @@
+popTimeout = $timeout;
+ }
+
+ /**
+ * @param null|float $timeout seconds
+ */
+ public function wait(Closure $closure, ?float $timeout = null)
+ {
+ if ($timeout === null) {
+ $timeout = $this->popTimeout;
+ }
+
+ $channel = new Channel(1);
+ Coroutine::create(function () use ($channel, $closure) {
+ try {
+ $result = $closure();
+ } catch (Throwable $exception) {
+ $result = new ExceptionThrower($exception);
+ } finally {
+ $channel->push($result ?? null, $this->pushTimeout);
+ }
+ });
+
+ $result = $channel->pop($timeout);
+ if ($result === false && $channel->isTimeout()) {
+ throw new WaitTimeoutException(sprintf('Channel wait failed, reason: Timed out for %s s', $timeout));
+ }
+ if ($result instanceof ExceptionThrower) {
+ throw $result->getThrowable();
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayApplicationInterface.php b/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayApplicationInterface.php
new file mode 100644
index 0000000..b4e296a
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayApplicationInterface.php
@@ -0,0 +1,83 @@
+
+ *
+ * @param string $gateway
+ * @param array $params
+ *
+ * @return Collection|Response
+ */
+ public function pay($gateway, $params);
+
+ /**
+ * Query an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @return Collection
+ */
+ public function find($order, string $type);
+
+ /**
+ * Refund an order.
+ *
+ * @author yansongda
+ *
+ * @return Collection
+ */
+ public function refund(array $order);
+
+ /**
+ * Cancel an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @return Collection
+ */
+ public function cancel($order);
+
+ /**
+ * Close an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @return Collection
+ */
+ public function close($order);
+
+ /**
+ * Verify a request.
+ *
+ * @author yansongda
+ *
+ * @param string|array|null $content
+ *
+ * @return Collection
+ */
+ public function verify($content, bool $refund);
+
+ /**
+ * Echo success to server.
+ *
+ * @author yansongda
+ *
+ * @return Response
+ */
+ public function success();
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayInterface.php b/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayInterface.php
new file mode 100644
index 0000000..8286763
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Contracts/GatewayInterface.php
@@ -0,0 +1,20 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @return Collection|Response
+ */
+ public function pay($endpoint, array $payload);
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events.php b/server/addons/epay/library/v2/Yansongda/Pay/Events.php
new file mode 100644
index 0000000..a747305
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events.php
@@ -0,0 +1,98 @@
+
+ *
+ * @method static Event dispatch(Event $event) Dispatches an event to all registered listeners
+ * @method static array getListeners($eventName = null) Gets the listeners of a specific event or all listeners sorted by descending priority.
+ * @method static int|void getListenerPriority($eventName, $listener) Gets the listener priority for a specific event.
+ * @method static bool hasListeners($eventName = null) Checks whether an event has any registered listeners.
+ * @method static void addListener($eventName, $listener, $priority = 0) Adds an event listener that listens on the specified events.
+ * @method static removeListener($eventName, $listener) Removes an event listener from the specified events.
+ * @method static void addSubscriber(EventSubscriberInterface $subscriber) Adds an event subscriber.
+ * @method static void removeSubscriber(EventSubscriberInterface $subscriber)
+ */
+class Events
+{
+ /**
+ * dispatcher.
+ *
+ * @var EventDispatcher
+ */
+ protected static $dispatcher;
+
+ /**
+ * Forward call.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @throws Exception
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $args)
+ {
+ return call_user_func_array([self::getDispatcher(), $method], $args);
+ }
+
+ /**
+ * Forward call.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @throws Exception
+ *
+ * @return mixed
+ */
+ public function __call($method, $args)
+ {
+ return call_user_func_array([self::getDispatcher(), $method], $args);
+ }
+
+ /**
+ * setDispatcher.
+ *
+ * @author yansongda
+ */
+ public static function setDispatcher(EventDispatcher $dispatcher)
+ {
+ self::$dispatcher = $dispatcher;
+ }
+
+ /**
+ * getDispatcher.
+ *
+ * @author yansongda
+ */
+ public static function getDispatcher(): EventDispatcher
+ {
+ if (self::$dispatcher) {
+ return self::$dispatcher;
+ }
+
+ return self::$dispatcher = self::createDispatcher();
+ }
+
+ /**
+ * createDispatcher.
+ *
+ * @author yansongda
+ */
+ public static function createDispatcher(): EventDispatcher
+ {
+ return new EventDispatcher();
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequested.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequested.php
new file mode 100644
index 0000000..845ac87
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequested.php
@@ -0,0 +1,31 @@
+endpoint = $endpoint;
+ $this->result = $result;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequesting.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequesting.php
new file mode 100644
index 0000000..14c9fdd
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/ApiRequesting.php
@@ -0,0 +1,31 @@
+endpoint = $endpoint;
+ $this->payload = $payload;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/Event.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/Event.php
new file mode 100644
index 0000000..1ef883c
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/Event.php
@@ -0,0 +1,40 @@
+
+ */
+ public function __construct(string $driver, string $gateway)
+ {
+ $this->driver = $driver;
+ $this->gateway = $gateway;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/MethodCalled.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/MethodCalled.php
new file mode 100644
index 0000000..d199f23
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/MethodCalled.php
@@ -0,0 +1,33 @@
+
+ */
+ public function __construct(string $driver, string $gateway, string $endpoint, array $payload = [])
+ {
+ $this->endpoint = $endpoint;
+ $this->payload = $payload;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarted.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarted.php
new file mode 100644
index 0000000..845d93c
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarted.php
@@ -0,0 +1,31 @@
+endpoint = $endpoint;
+ $this->payload = $payload;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarting.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarting.php
new file mode 100644
index 0000000..ad73d9d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/PayStarting.php
@@ -0,0 +1,23 @@
+params = $params;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/RequestReceived.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/RequestReceived.php
new file mode 100644
index 0000000..3620b12
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/RequestReceived.php
@@ -0,0 +1,25 @@
+
+ */
+ public function __construct(string $driver, string $gateway, array $data)
+ {
+ $this->data = $data;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Events/SignFailed.php b/server/addons/epay/library/v2/Yansongda/Pay/Events/SignFailed.php
new file mode 100644
index 0000000..4e45932
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Events/SignFailed.php
@@ -0,0 +1,25 @@
+
+ */
+ public function __construct(string $driver, string $gateway, array $data)
+ {
+ $this->data = $data;
+
+ parent::__construct($driver, $gateway);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/BusinessException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/BusinessException.php
new file mode 100644
index 0000000..8e94a4f
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/BusinessException.php
@@ -0,0 +1,19 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ */
+ public function __construct($message, $raw = [])
+ {
+ parent::__construct('ERROR_BUSINESS: '.$message, $raw, self::ERROR_BUSINESS);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/Exception.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/Exception.php
new file mode 100644
index 0000000..9d007ad
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/Exception.php
@@ -0,0 +1,44 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ * @param int|string $code
+ */
+ public function __construct($message = '', $raw = [], $code = self::UNKNOWN_ERROR)
+ {
+ $message = '' === $message ? 'Unknown Error' : $message;
+ $this->raw = is_array($raw) ? $raw : [$raw];
+
+ parent::__construct($message, intval($code));
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/GatewayException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/GatewayException.php
new file mode 100644
index 0000000..e844801
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/GatewayException.php
@@ -0,0 +1,20 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ * @param int $code
+ */
+ public function __construct($message, $raw = [], $code = self::ERROR_GATEWAY)
+ {
+ parent::__construct('ERROR_GATEWAY: '.$message, $raw, $code);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidArgumentException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidArgumentException.php
new file mode 100644
index 0000000..2ecb16b
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidArgumentException.php
@@ -0,0 +1,19 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ */
+ public function __construct($message, $raw = [])
+ {
+ parent::__construct('INVALID_ARGUMENT: '.$message, $raw, self::INVALID_ARGUMENT);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidConfigException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidConfigException.php
new file mode 100644
index 0000000..5719310
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidConfigException.php
@@ -0,0 +1,19 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ */
+ public function __construct($message, $raw = [])
+ {
+ parent::__construct('INVALID_CONFIG: '.$message, $raw, self::INVALID_CONFIG);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidGatewayException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidGatewayException.php
new file mode 100644
index 0000000..3f4067d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidGatewayException.php
@@ -0,0 +1,19 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ */
+ public function __construct($message, $raw = [])
+ {
+ parent::__construct('INVALID_GATEWAY: '.$message, $raw, self::INVALID_GATEWAY);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidSignException.php b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidSignException.php
new file mode 100644
index 0000000..135c8a5
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Exceptions/InvalidSignException.php
@@ -0,0 +1,19 @@
+
+ *
+ * @param string $message
+ * @param array|string $raw
+ */
+ public function __construct($message, $raw = [])
+ {
+ parent::__construct('INVALID_SIGN: '.$message, $raw, self::INVALID_SIGN);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay.php
new file mode 100644
index 0000000..30df2e7
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay.php
@@ -0,0 +1,423 @@
+ 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ self::MODE_SERVICE => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ self::MODE_DEV => 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8',
+ ];
+
+ /**
+ * Alipay payload.
+ *
+ * @var array
+ */
+ protected $payload;
+
+ /**
+ * Alipay gateway.
+ *
+ * @var string
+ */
+ protected $gateway;
+
+ /**
+ * extends.
+ *
+ * @var array
+ */
+ protected $extends;
+
+ /**
+ * Bootstrap.
+ *
+ * @author yansongda
+ *
+ * @throws \Exception
+ */
+ public function __construct(Config $config)
+ {
+ $this->gateway = Support::create($config)->getBaseUri();
+ $this->payload = [
+ 'app_id' => $config->get('app_id'),
+ 'method' => '',
+ 'format' => 'JSON',
+ 'charset' => 'utf-8',
+ 'sign_type' => 'RSA2',
+ 'version' => '1.0',
+ 'return_url' => $config->get('return_url'),
+ 'notify_url' => $config->get('notify_url'),
+ 'timestamp' => date('Y-m-d H:i:s'),
+ 'sign' => '',
+ 'biz_content' => '',
+ 'app_auth_token' => $config->get('app_auth_token'),
+ ];
+
+ if ($config->get('app_cert_public_key') && $config->get('alipay_root_cert')) {
+ $this->payload['app_cert_sn'] = Support::getCertSN($config->get('app_cert_public_key'));
+ $this->payload['alipay_root_cert_sn'] = Support::getRootCertSN($config->get('alipay_root_cert'));
+ }
+ }
+
+ /**
+ * Magic pay.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $params
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidConfigException
+ * @throws InvalidGatewayException
+ * @throws InvalidSignException
+ *
+ * @return Response|Collection
+ */
+ public function __call($method, $params)
+ {
+ if (isset($this->extends[$method])) {
+ return $this->makeExtend($method, ...$params);
+ }
+
+ return $this->pay($method, ...$params);
+ }
+
+ /**
+ * Pay an order.
+ *
+ * @author yansongda
+ *
+ * @param string $gateway
+ * @param array $params
+ *
+ * @throws InvalidGatewayException
+ *
+ * @return Response|Collection
+ */
+ public function pay($gateway, $params = [])
+ {
+ Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params));
+
+ $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
+ $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
+
+ unset($params['return_url'], $params['notify_url']);
+
+ $this->payload['biz_content'] = json_encode($params);
+
+ $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+ if (class_exists($gateway)) {
+ return $this->makePay($gateway);
+ }
+
+ throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
+ }
+
+ /**
+ * Verify sign.
+ *
+ * @author yansongda
+ *
+ * @param array|null $data
+ *
+ * @throws InvalidSignException
+ * @throws InvalidConfigException
+ */
+ public function verify($data = null, bool $refund = false): Collection
+ {
+ if (is_null($data)) {
+ $request = Request::createFromGlobals();
+
+ $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
+ }
+
+ if (isset($data['fund_bill_list'])) {
+ $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
+ }
+
+ Events::dispatch(new Events\RequestReceived('Alipay', '', $data));
+
+ if (Support::verifySign($data)) {
+ return new Collection($data);
+ }
+
+ Events::dispatch(new Events\SignFailed('Alipay', '', $data));
+
+ throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
+ }
+
+ /**
+ * Query an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function find($order, string $type = 'wap'): Collection
+ {
+ $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+ if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+ throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+ }
+
+ $config = call_user_func([new $gateway(), 'find'], $order);
+
+ $this->payload['method'] = $config['method'];
+ $this->payload['biz_content'] = $config['biz_content'];
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
+
+ return Support::requestApi($this->payload);
+ }
+
+ /**
+ * Refund an order.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function refund(array $order): Collection
+ {
+ $this->payload['method'] = 'alipay.trade.refund';
+ $this->payload['biz_content'] = json_encode($order);
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
+
+ return Support::requestApi($this->payload);
+ }
+
+ /**
+ * Cancel an order.
+ *
+ * @author yansongda
+ *
+ * @param array|string $order
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function cancel($order): Collection
+ {
+ $this->payload['method'] = 'alipay.trade.cancel';
+ $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
+
+ return Support::requestApi($this->payload);
+ }
+
+ /**
+ * Close an order.
+ *
+ * @param string|array $order
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function close($order): Collection
+ {
+ $this->payload['method'] = 'alipay.trade.close';
+ $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
+
+ return Support::requestApi($this->payload);
+ }
+
+ /**
+ * Download bill.
+ *
+ * @author yansongda
+ *
+ * @param string|array $bill
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function download($bill): string
+ {
+ $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
+ $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
+
+ $result = Support::requestApi($this->payload);
+
+ return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
+ }
+
+ /**
+ * Reply success to alipay.
+ *
+ * @author yansongda
+ */
+ public function success(): Response
+ {
+ Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway));
+
+ return new Response('success');
+ }
+
+ /**
+ * extend.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function extend(string $method, callable $function, bool $now = true): ?Collection
+ {
+ if (!$now && !method_exists($this, $method)) {
+ $this->extends[$method] = $function;
+
+ return null;
+ }
+
+ $customize = $function($this->payload);
+
+ if (!is_array($customize) && !($customize instanceof Collection)) {
+ throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+ }
+
+ Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize));
+
+ if (is_array($customize)) {
+ $this->payload = $customize;
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ return Support::requestApi($this->payload);
+ }
+
+ return $customize;
+ }
+
+ /**
+ * Make pay gateway.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidGatewayException
+ *
+ * @return Response|Collection
+ */
+ protected function makePay(string $gateway)
+ {
+ $app = new $gateway();
+
+ if ($app instanceof GatewayInterface) {
+ return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+ return '' !== $value && !is_null($value);
+ }));
+ }
+
+ throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+ }
+
+ /**
+ * makeExtend.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ protected function makeExtend(string $method, array ...$params): Collection
+ {
+ $params = count($params) >= 1 ? $params[0] : $params;
+
+ $function = $this->extends[$method];
+
+ $customize = $function($this->payload, $params);
+
+ if (!is_array($customize) && !($customize instanceof Collection)) {
+ throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
+ }
+
+ Events::dispatch(new Events\MethodCalled(
+ 'Alipay',
+ 'extend - '.$method,
+ $this->gateway,
+ is_array($customize) ? $customize : $customize->toArray()
+ ));
+
+ if (is_array($customize)) {
+ $this->payload = $customize;
+ $this->payload['sign'] = Support::generateSign($this->payload);
+
+ return Support::requestApi($this->payload);
+ }
+
+ return $customize;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/AppGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/AppGateway.php
new file mode 100644
index 0000000..860b44b
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/AppGateway.php
@@ -0,0 +1,38 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws InvalidConfigException
+ * @throws InvalidArgumentException
+ */
+ public function pay($endpoint, array $payload): Response
+ {
+ $payload['method'] = 'alipay.trade.app.pay';
+
+ $biz_array = json_decode($payload['biz_content'], true);
+ if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+ $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+ }
+ $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => 'QUICK_MSECURITY_PAY']));
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'App', $endpoint, $payload));
+
+ return new Response(http_build_query($payload));
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Gateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Gateway.php
new file mode 100644
index 0000000..5f57cc3
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Gateway.php
@@ -0,0 +1,40 @@
+
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct()
+ {
+ $this->mode = Support::getInstance()->mode;
+ }
+
+ /**
+ * Pay an order.
+ *
+ * @author yansongda
+ *
+ * @param string $endpoint
+ *
+ * @return Collection
+ */
+ abstract public function pay($endpoint, array $payload);
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/MiniGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/MiniGateway.php
new file mode 100644
index 0000000..3868c3d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/MiniGateway.php
@@ -0,0 +1,46 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ *
+ * @see https://docs.alipay.com/mini/introduce/pay
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $biz_array = json_decode($payload['biz_content'], true);
+ if (empty($biz_array['buyer_id']) && empty($biz_array['buyer_open_id'])) {
+ throw new InvalidArgumentException('buyer_id or buyer_open_id required');
+ }
+ if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+ $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+ }
+ $payload['biz_content'] = json_encode($biz_array);
+ $payload['method'] = 'alipay.trade.create';
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'Mini', $endpoint, $payload));
+
+ return Support::requestApi($payload);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/PosGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/PosGateway.php
new file mode 100644
index 0000000..de04761
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/PosGateway.php
@@ -0,0 +1,47 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws InvalidArgumentException
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['method'] = 'alipay.trade.pay';
+ $biz_array = json_decode($payload['biz_content'], true);
+ if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+ $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+ }
+ $payload['biz_content'] = json_encode(array_merge(
+ $biz_array,
+ [
+ 'product_code' => 'FACE_TO_FACE_PAYMENT',
+ 'scene' => 'bar_code',
+ ]
+ ));
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'Pos', $endpoint, $payload));
+
+ return Support::requestApi($payload);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/RefundGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/RefundGateway.php
new file mode 100644
index 0000000..abbb71f
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/RefundGateway.php
@@ -0,0 +1,21 @@
+
+ *
+ * @param $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/ScanGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/ScanGateway.php
new file mode 100644
index 0000000..9d6cbc3
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/ScanGateway.php
@@ -0,0 +1,41 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['method'] = 'alipay.trade.precreate';
+ $biz_array = json_decode($payload['biz_content'], true);
+ if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+ $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+ }
+ $payload['biz_content'] = json_encode(array_merge($biz_array, ['product_code' => '']));
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'Scan', $endpoint, $payload));
+
+ return Support::requestApi($payload);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Support.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Support.php
new file mode 100644
index 0000000..289bb94
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/Support.php
@@ -0,0 +1,452 @@
+
+ *
+ * @property string app_id alipay app_id
+ * @property string ali_public_key
+ * @property string private_key
+ * @property array http http options
+ * @property string mode current mode
+ * @property array log log options
+ * @property string pid ali pid
+ */
+class Support
+{
+ use HasHttpRequest;
+
+ /**
+ * Alipay gateway.
+ *
+ * @var string
+ */
+ protected $baseUri;
+
+ /**
+ * Config.
+ *
+ * @var Config
+ */
+ protected $config;
+
+ /**
+ * Instance.
+ *
+ * @var Support
+ */
+ private static $instance;
+
+ /**
+ * Bootstrap.
+ *
+ * @author yansongda
+ */
+ private function __construct(Config $config)
+ {
+ $this->baseUri = Alipay::URL[$config->get('mode', Alipay::MODE_NORMAL)];
+ $this->config = $config;
+
+ $this->setHttpOptions();
+ }
+
+ /**
+ * __get.
+ *
+ * @author yansongda
+ *
+ * @param $key
+ *
+ * @return mixed|Config|null
+ */
+ public function __get($key)
+ {
+ return $this->getConfig($key);
+ }
+
+ /**
+ * create.
+ *
+ * @author yansongda
+ *
+ * @return Support
+ */
+ public static function create(Config $config)
+ {
+ if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+ self::$instance = new self($config);
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * getInstance.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return Support
+ */
+ public static function getInstance()
+ {
+ if (is_null(self::$instance)) {
+ throw new InvalidArgumentException('You Should [Create] First Before Using');
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * clear.
+ *
+ * @author yansongda
+ */
+ public function clear()
+ {
+ self::$instance = null;
+ }
+
+ /**
+ * Get Alipay API result.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public static function requestApi(array $data): Collection
+ {
+ Events::dispatch(new Events\ApiRequesting('Alipay', '', self::$instance->getBaseUri(), $data));
+
+ $data = array_filter($data, function ($value) {
+ return ('' == $value || is_null($value)) ? false : true;
+ });
+
+ $result = json_decode(self::$instance->post('', $data), true);
+
+ Events::dispatch(new Events\ApiRequested('Alipay', '', self::$instance->getBaseUri(), $result));
+
+ return self::processingApiResult($data, $result);
+ }
+
+ /**
+ * Generate sign.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidConfigException
+ */
+ public static function generateSign(array $params): string
+ {
+ $privateKey = self::$instance->private_key;
+
+ if (is_null($privateKey)) {
+ throw new InvalidConfigException('Missing Alipay Config -- [private_key]');
+ }
+
+ if (Str::endsWith($privateKey, '.pem')) {
+ $privateKey = openssl_pkey_get_private(
+ Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey
+ );
+ } else {
+ $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
+ wordwrap($privateKey, 64, "\n", true).
+ "\n-----END RSA PRIVATE KEY-----";
+ }
+
+ openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256);
+
+ $sign = base64_encode($sign);
+
+ Log::debug('Alipay Generate Sign', [$params, $sign]);
+
+ if (is_resource($privateKey)) {
+ openssl_free_key($privateKey);
+ }
+
+ return $sign;
+ }
+
+ /**
+ * Verify sign.
+ *
+ * @author yansongda
+ *
+ * @param bool $sync
+ * @param string|null $sign
+ *
+ * @throws InvalidConfigException
+ */
+ public static function verifySign(array $data, $sync = false, $sign = null): bool
+ {
+ $publicKey = self::$instance->ali_public_key;
+
+ if (is_null($publicKey)) {
+ throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]');
+ }
+
+ if (Str::endsWith($publicKey, '.crt')) {
+ $publicKey = file_get_contents($publicKey);
+ } elseif (Str::endsWith($publicKey, '.pem')) {
+ $publicKey = openssl_pkey_get_public(
+ Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey
+ );
+ } else {
+ $publicKey = "-----BEGIN PUBLIC KEY-----\n".
+ wordwrap($publicKey, 64, "\n", true).
+ "\n-----END PUBLIC KEY-----";
+ }
+
+ $sign = $sign ?? $data['sign'];
+
+ $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true);
+
+ $isVerify = 1 === openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
+
+ if (is_resource($publicKey)) {
+ openssl_free_key($publicKey);
+ }
+
+ return $isVerify;
+ }
+
+ /**
+ * Get signContent that is to be signed.
+ *
+ * @author yansongda
+ *
+ * @param bool $verify
+ */
+ public static function getSignContent(array $data, $verify = false): string
+ {
+ ksort($data);
+
+ $stringToBeSigned = '';
+ foreach ($data as $k => $v) {
+ if ($verify && 'sign' != $k && 'sign_type' != $k) {
+ $stringToBeSigned .= $k.'='.$v.'&';
+ }
+ if (!$verify && '' !== $v && !is_null($v) && 'sign' != $k && '@' != substr($v, 0, 1)) {
+ $stringToBeSigned .= $k.'='.$v.'&';
+ }
+ }
+
+ Log::debug('Alipay Generate Sign Content Before Trim', [$data, $stringToBeSigned]);
+
+ return trim($stringToBeSigned, '&');
+ }
+
+ /**
+ * Convert encoding.
+ *
+ * @author yansongda
+ *
+ * @param string|array $data
+ * @param string $to
+ * @param string $from
+ */
+ public static function encoding($data, $to = 'utf-8', $from = 'gb2312'): array
+ {
+ return Arr::encoding((array) $data, $to, $from);
+ }
+
+ /**
+ * Get service config.
+ *
+ * @author yansongda
+ *
+ * @param string|null $key
+ * @param mixed|null $default
+ *
+ * @return mixed|null
+ */
+ public function getConfig($key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return $this->config->all();
+ }
+
+ if ($this->config->has($key)) {
+ return $this->config[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Get Base Uri.
+ *
+ * @author yansongda
+ *
+ * @return string
+ */
+ public function getBaseUri()
+ {
+ return $this->baseUri;
+ }
+
+ /**
+ * 生成应用证书SN.
+ *
+ * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+ *
+ * @param $certPath
+ *
+ * @throws /Exception
+ */
+ public static function getCertSN($certPath): string
+ {
+ if (!is_file($certPath)) {
+ throw new Exception('unknown certPath -- [getCertSN]');
+ }
+ $x509data = file_get_contents($certPath);
+ if (false === $x509data) {
+ throw new Exception('Alipay CertSN Error -- [getCertSN]');
+ }
+ openssl_x509_read($x509data);
+ $certdata = openssl_x509_parse($x509data);
+ if (empty($certdata)) {
+ throw new Exception('Alipay openssl_x509_parse Error -- [getCertSN]');
+ }
+ $issuer_arr = [];
+ foreach ($certdata['issuer'] as $key => $val) {
+ $issuer_arr[] = $key.'='.$val;
+ }
+ $issuer = implode(',', array_reverse($issuer_arr));
+ Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]);
+
+ return md5($issuer.$certdata['serialNumber']);
+ }
+
+ /**
+ * 生成支付宝根证书SN.
+ *
+ * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+ *
+ * @param $certPath
+ *
+ * @return string
+ *
+ * @throws /Exception
+ */
+ public static function getRootCertSN($certPath)
+ {
+ if (!is_file($certPath)) {
+ throw new Exception('unknown certPath -- [getRootCertSN]');
+ }
+ $x509data = file_get_contents($certPath);
+ if (false === $x509data) {
+ throw new Exception('Alipay CertSN Error -- [getRootCertSN]');
+ }
+ $kCertificateEnd = '-----END CERTIFICATE-----';
+ $certStrList = explode($kCertificateEnd, $x509data);
+ $md5_arr = [];
+ foreach ($certStrList as $one) {
+ if (!empty(trim($one))) {
+ $_x509data = $one.$kCertificateEnd;
+ openssl_x509_read($_x509data);
+ $_certdata = openssl_x509_parse($_x509data);
+ if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) {
+ $issuer_arr = [];
+ foreach ($_certdata['issuer'] as $key => $val) {
+ $issuer_arr[] = $key.'='.$val;
+ }
+ $_issuer = implode(',', array_reverse($issuer_arr));
+ if (0 === strpos($_certdata['serialNumber'], '0x')) {
+ $serialNumber = self::bchexdec($_certdata['serialNumber']);
+ } else {
+ $serialNumber = $_certdata['serialNumber'];
+ }
+ $md5_arr[] = md5($_issuer.$serialNumber);
+ Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]);
+ }
+ }
+ }
+
+ return implode('_', $md5_arr);
+ }
+
+ /**
+ * processingApiResult.
+ *
+ * @author yansongda
+ *
+ * @param $data
+ * @param $result
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ protected static function processingApiResult($data, $result): Collection
+ {
+ $method = str_replace('.', '_', $data['method']).'_response';
+
+ if (!isset($result['sign']) || '10000' != $result[$method]['code']) {
+ throw new GatewayException('Get Alipay API Error:'.$result[$method]['msg'].(isset($result[$method]['sub_code']) ? (' - '.$result[$method]['sub_code']) : ''), $result);
+ }
+
+ if (self::verifySign($result[$method], true, $result['sign'])) {
+ return new Collection($result[$method]);
+ }
+
+ Events::dispatch(new Events\SignFailed('Alipay', '', $result));
+
+ throw new InvalidSignException('Alipay Sign Verify FAILED', $result);
+ }
+
+ /**
+ * Set Http options.
+ *
+ * @author yansongda
+ */
+ protected function setHttpOptions(): self
+ {
+ if ($this->config->has('http') && is_array($this->config->get('http'))) {
+ $this->config->forget('http.base_uri');
+ $this->httpOptions = $this->config->get('http');
+ }
+
+ return $this;
+ }
+
+ /**
+ * 0x转高精度数字.
+ *
+ * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
+ *
+ * @param $hex
+ *
+ * @return int|string
+ */
+ private static function bchexdec($hex)
+ {
+ $dec = 0;
+ $len = strlen($hex);
+ for ($i = 1; $i <= $len; ++$i) {
+ if (ctype_xdigit($hex[$i - 1])) {
+ $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
+ }
+ }
+
+ return str_replace('.00', '', $dec);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/TransferGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/TransferGateway.php
new file mode 100644
index 0000000..1015ac3
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/TransferGateway.php
@@ -0,0 +1,49 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['method'] = 'alipay.fund.trans.uni.transfer';
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'Transfer', $endpoint, $payload));
+
+ return Support::requestApi($payload);
+ }
+
+ /**
+ * Find.
+ *
+ * @author yansongda
+ *
+ * @param $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'method' => 'alipay.fund.trans.order.query',
+ 'biz_content' => json_encode(is_array($order) ? $order : ['out_biz_no' => $order]),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WapGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WapGateway.php
new file mode 100644
index 0000000..dda8c65
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WapGateway.php
@@ -0,0 +1,26 @@
+
+ */
+ protected function getMethod(): string
+ {
+ return 'alipay.trade.wap.pay';
+ }
+
+ /**
+ * Get productCode config.
+ *
+ * @author yansongda
+ */
+ protected function getProductCode(): string
+ {
+ return 'QUICK_WAP_WAY';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WebGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WebGateway.php
new file mode 100644
index 0000000..37a0cd9
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Alipay/WebGateway.php
@@ -0,0 +1,104 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws InvalidConfigException
+ * @throws InvalidArgumentException
+ */
+ public function pay($endpoint, array $payload): Response
+ {
+ $biz_array = json_decode($payload['biz_content'], true);
+ $biz_array['product_code'] = $this->getProductCode();
+
+ $method = $biz_array['http_method'] ?? 'POST';
+
+ unset($biz_array['http_method']);
+ if ((Alipay::MODE_SERVICE === $this->mode) && (!empty(Support::getInstance()->pid))) {
+ $biz_array['extend_params'] = is_array($biz_array['extend_params']) ? array_merge(['sys_service_provider_id' => Support::getInstance()->pid], $biz_array['extend_params']) : ['sys_service_provider_id' => Support::getInstance()->pid];
+ }
+ $payload['method'] = $this->getMethod();
+ $payload['biz_content'] = json_encode($biz_array);
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Alipay', 'Web/Wap', $endpoint, $payload));
+
+ return $this->buildPayHtml($endpoint, $payload, $method);
+ }
+
+ /**
+ * Find.
+ *
+ * @author yansongda
+ *
+ * @param $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => json_encode(is_array($order) ? $order : ['out_trade_no' => $order]),
+ ];
+ }
+
+ /**
+ * Build Html response.
+ *
+ * @author yansongda
+ *
+ * @param string $endpoint
+ * @param array $payload
+ * @param string $method
+ */
+ protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response
+ {
+ if ('GET' === strtoupper($method)) {
+ return new RedirectResponse($endpoint.'&'.http_build_query($payload));
+ }
+
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response($sHtml);
+ }
+
+ /**
+ * Get method config.
+ *
+ * @author yansongda
+ */
+ protected function getMethod(): string
+ {
+ return 'alipay.trade.page.pay';
+ }
+
+ /**
+ * Get productCode config.
+ *
+ * @author yansongda
+ */
+ protected function getProductCode(): string
+ {
+ return 'FAST_INSTANT_TRADE_PAY';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat.php
new file mode 100644
index 0000000..80fcebd
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat.php
@@ -0,0 +1,366 @@
+ 'https://api.mch.weixin.qq.com/',
+ self::MODE_DEV => 'https://api.mch.weixin.qq.com/xdc/apiv2sandbox/',
+ self::MODE_HK => 'https://apihk.mch.weixin.qq.com/',
+ self::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
+ self::MODE_US => 'https://apius.mch.weixin.qq.com/',
+ ];
+
+ /**
+ * Wechat payload.
+ *
+ * @var array
+ */
+ protected $payload;
+
+ /**
+ * Wechat gateway.
+ *
+ * @var string
+ */
+ protected $gateway;
+
+ /**
+ * Bootstrap.
+ *
+ * @author yansongda
+ *
+ * @throws Exception
+ */
+ public function __construct(Config $config)
+ {
+ $this->gateway = Support::create($config)->getBaseUri();
+ $this->payload = [
+ 'appid' => $config->get('app_id', ''),
+ 'mch_id' => $config->get('mch_id', ''),
+ 'nonce_str' => Str::random(),
+ 'notify_url' => $config->get('notify_url', ''),
+ 'sign' => '',
+ 'trade_type' => '',
+ 'spbill_create_ip' => Request::createFromGlobals()->getClientIp(),
+ ];
+
+ if ($config->get('mode', self::MODE_NORMAL) === static::MODE_SERVICE) {
+ $this->payload = array_merge($this->payload, [
+ 'sub_mch_id' => $config->get('sub_mch_id'),
+ 'sub_appid' => $config->get('sub_app_id', ''),
+ ]);
+ }
+ }
+
+ /**
+ * Magic pay.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param string $params
+ *
+ * @throws InvalidGatewayException
+ *
+ * @return Response|Collection
+ */
+ public function __call($method, $params)
+ {
+ return self::pay($method, ...$params);
+ }
+
+ /**
+ * Pay an order.
+ *
+ * @author yansongda
+ *
+ * @param string $gateway
+ * @param array $params
+ *
+ * @throws InvalidGatewayException
+ *
+ * @return Response|Collection
+ */
+ public function pay($gateway, $params = [])
+ {
+ Events::dispatch(new Events\PayStarting('Wechat', $gateway, $params));
+
+ $this->payload = array_merge($this->payload, $params);
+
+ $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
+
+ if (class_exists($gateway)) {
+ return $this->makePay($gateway);
+ }
+
+ throw new InvalidGatewayException("Pay Gateway [{$gateway}] Not Exists");
+ }
+
+ /**
+ * Verify data.
+ *
+ * @author yansongda
+ *
+ * @param string|null $content
+ *
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function verify($content = null, bool $refund = false): Collection
+ {
+ $content = $content ?? Request::createFromGlobals()->getContent();
+
+ Events::dispatch(new Events\RequestReceived('Wechat', '', [$content]));
+
+ $data = Support::fromXml($content);
+ if ($refund) {
+ $decrypt_data = Support::decryptRefundContents($data['req_info']);
+ $data = array_merge(Support::fromXml($decrypt_data), $data);
+ }
+
+ Log::debug('Resolved The Received Wechat Request Data', $data);
+
+ if ($refund || Support::generateSign($data) === $data['sign']) {
+ return new Collection($data);
+ }
+
+ Events::dispatch(new Events\SignFailed('Wechat', '', $data));
+
+ throw new InvalidSignException('Wechat Sign Verify FAILED', $data);
+ }
+
+ /**
+ * Query an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @throws GatewayException
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function find($order, string $type = 'wap'): Collection
+ {
+ unset($this->payload['spbill_create_ip']);
+
+ $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
+
+ if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
+ throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
+ }
+
+ $config = call_user_func([new $gateway(), 'find'], $order);
+
+ $this->payload = Support::filterPayload($this->payload, $config['order']);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Find', $this->gateway, $this->payload));
+
+ return Support::requestApi(
+ $config['endpoint'],
+ $this->payload,
+ $config['cert']
+ );
+ }
+
+ /**
+ * Refund an order.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function refund(array $order): Collection
+ {
+ unset($this->payload['spbill_create_ip']);
+
+ $this->payload = Support::filterPayload($this->payload, $order, true);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Refund', $this->gateway, $this->payload));
+
+ return Support::requestApi(
+ 'secapi/pay/refund',
+ $this->payload,
+ true
+ );
+ }
+
+ /**
+ * Cancel an order.
+ *
+ * @author yansongda
+ *
+ * @param array $order
+ *
+ * @throws GatewayException
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function cancel($order): Collection
+ {
+ unset($this->payload['spbill_create_ip']);
+
+ $this->payload = Support::filterPayload($this->payload, $order);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Cancel', $this->gateway, $this->payload));
+
+ return Support::requestApi(
+ 'secapi/pay/reverse',
+ $this->payload,
+ true
+ );
+ }
+
+ /**
+ * Close an order.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ *
+ * @throws GatewayException
+ * @throws InvalidSignException
+ * @throws InvalidArgumentException
+ */
+ public function close($order): Collection
+ {
+ unset($this->payload['spbill_create_ip']);
+
+ $this->payload = Support::filterPayload($this->payload, $order);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Close', $this->gateway, $this->payload));
+
+ return Support::requestApi('pay/closeorder', $this->payload);
+ }
+
+ /**
+ * Echo success to server.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidArgumentException
+ */
+ public function success(): Response
+ {
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Success', $this->gateway));
+
+ return new Response(
+ Support::toXml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']),
+ 200,
+ ['Content-Type' => 'application/xml']
+ );
+ }
+
+ /**
+ * Download the bill.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ */
+ public function download(array $params): string
+ {
+ unset($this->payload['spbill_create_ip']);
+
+ $this->payload = Support::filterPayload($this->payload, $params, true);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'Download', $this->gateway, $this->payload));
+
+ $result = Support::getInstance()->post(
+ 'pay/downloadbill',
+ Support::getInstance()->toXml($this->payload)
+ );
+
+ if (is_array($result)) {
+ throw new GatewayException('Get Wechat API Error: '.$result['return_msg'], $result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Make pay gateway.
+ *
+ * @author yansongda
+ *
+ * @param string $gateway
+ *
+ * @throws InvalidGatewayException
+ *
+ * @return Response|Collection
+ */
+ protected function makePay($gateway)
+ {
+ $app = new $gateway();
+
+ if ($app instanceof GatewayInterface) {
+ return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
+ return '' !== $value && !is_null($value);
+ }));
+ }
+
+ throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/AppGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/AppGateway.php
new file mode 100644
index 0000000..b37b965
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/AppGateway.php
@@ -0,0 +1,62 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ * @throws Exception
+ */
+ public function pay($endpoint, array $payload): Response
+ {
+ $payload['appid'] = Support::getInstance()->appid;
+ $payload['trade_type'] = $this->getTradeType();
+
+ if (Wechat::MODE_SERVICE === $this->mode) {
+ $payload['sub_appid'] = Support::getInstance()->sub_appid;
+ }
+
+ $pay_request = [
+ 'appid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_appid'] : $payload['appid'],
+ 'partnerid' => Wechat::MODE_SERVICE === $this->mode ? $payload['sub_mch_id'] : $payload['mch_id'],
+ 'prepayid' => $this->preOrder($payload)->get('prepay_id'),
+ 'timestamp' => strval(time()),
+ 'noncestr' => Str::random(),
+ 'package' => 'Sign=WXPay',
+ ];
+ $pay_request['sign'] = Support::generateSign($pay_request);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'App', $endpoint, $pay_request));
+
+ return new JsonResponse($pay_request);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return 'APP';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Gateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Gateway.php
new file mode 100644
index 0000000..439a908
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Gateway.php
@@ -0,0 +1,88 @@
+
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct()
+ {
+ $this->mode = Support::getInstance()->mode;
+ }
+
+ /**
+ * Pay an order.
+ *
+ * @author yansongda
+ *
+ * @param string $endpoint
+ *
+ * @return Collection
+ */
+ abstract public function pay($endpoint, array $payload);
+
+ /**
+ * Find.
+ *
+ * @author yansongda
+ *
+ * @param string|array $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'endpoint' => 'pay/orderquery',
+ 'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+ 'cert' => false,
+ ];
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ *
+ * @return string
+ */
+ abstract protected function getTradeType();
+
+ /**
+ * Schedule an order.
+ *
+ * @author yansongda
+ *
+ * @param array $payload
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ protected function preOrder($payload): Collection
+ {
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\MethodCalled('Wechat', 'PreOrder', '', $payload));
+
+ return Support::requestApi('pay/unifiedorder', $payload);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/GroupRedpackGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/GroupRedpackGateway.php
new file mode 100644
index 0000000..de690d6
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/GroupRedpackGateway.php
@@ -0,0 +1,57 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['wxappid'] = $payload['appid'];
+ $payload['amt_type'] = 'ALL_RAND';
+
+ if (Wechat::MODE_SERVICE === $this->mode) {
+ $payload['msgappid'] = $payload['appid'];
+ }
+
+ unset($payload['appid'], $payload['trade_type'],
+ $payload['notify_url'], $payload['spbill_create_ip']);
+
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Group Redpack', $endpoint, $payload));
+
+ return Support::requestApi(
+ 'mmpaymkttransfers/sendgroupredpack',
+ $payload,
+ true
+ );
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return '';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MiniappGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MiniappGateway.php
new file mode 100644
index 0000000..8878dd2
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MiniappGateway.php
@@ -0,0 +1,35 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['appid'] = Support::getInstance()->miniapp_id;
+
+ if (Wechat::MODE_SERVICE === $this->mode) {
+ $payload['sub_appid'] = Support::getInstance()->sub_miniapp_id;
+ $this->payRequestUseSubAppId = true;
+ }
+
+ return parent::pay($endpoint, $payload);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MpGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MpGateway.php
new file mode 100644
index 0000000..1c7fd9d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/MpGateway.php
@@ -0,0 +1,59 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ * @throws Exception
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['trade_type'] = $this->getTradeType();
+
+ $pay_request = [
+ 'appId' => !$this->payRequestUseSubAppId ? $payload['appid'] : $payload['sub_appid'],
+ 'timeStamp' => strval(time()),
+ 'nonceStr' => Str::random(),
+ 'package' => 'prepay_id='.$this->preOrder($payload)->get('prepay_id'),
+ 'signType' => 'MD5',
+ ];
+ $pay_request['paySign'] = Support::generateSign($pay_request);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'JSAPI', $endpoint, $pay_request));
+
+ return new Collection($pay_request);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return 'JSAPI';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/PosGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/PosGateway.php
new file mode 100644
index 0000000..01061b6
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/PosGateway.php
@@ -0,0 +1,44 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ unset($payload['trade_type'], $payload['notify_url']);
+
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Pos', $endpoint, $payload));
+
+ return Support::requestApi('pay/micropay', $payload);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return 'MICROPAY';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RedpackGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RedpackGateway.php
new file mode 100644
index 0000000..db7d06f
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RedpackGateway.php
@@ -0,0 +1,61 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['wxappid'] = $payload['appid'];
+
+ if ('cli' !== php_sapi_name()) {
+ $payload['client_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+ }
+
+ if (Wechat::MODE_SERVICE === $this->mode) {
+ $payload['msgappid'] = $payload['appid'];
+ }
+
+ unset($payload['appid'], $payload['trade_type'],
+ $payload['notify_url'], $payload['spbill_create_ip']);
+
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Redpack', $endpoint, $payload));
+
+ return Support::requestApi(
+ 'mmpaymkttransfers/sendredpack',
+ $payload,
+ true
+ );
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return '';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RefundGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RefundGateway.php
new file mode 100644
index 0000000..e354cf5
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/RefundGateway.php
@@ -0,0 +1,50 @@
+
+ *
+ * @param $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'endpoint' => 'pay/refundquery',
+ 'order' => is_array($order) ? $order : ['out_trade_no' => $order],
+ 'cert' => false,
+ ];
+ }
+
+ /**
+ * Pay an order.
+ *
+ * @author yansongda
+ *
+ * @param string $endpoint
+ *
+ * @throws InvalidArgumentException
+ */
+ public function pay($endpoint, array $payload)
+ {
+ throw new InvalidArgumentException('Not Support Refund In Pay');
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidArgumentException
+ */
+ protected function getTradeType()
+ {
+ throw new InvalidArgumentException('Not Support Refund In Pay');
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/ScanGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/ScanGateway.php
new file mode 100644
index 0000000..1338665
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/ScanGateway.php
@@ -0,0 +1,44 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+ $payload['trade_type'] = $this->getTradeType();
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Scan', $endpoint, $payload));
+
+ return $this->preOrder($payload);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return 'NATIVE';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Support.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Support.php
new file mode 100644
index 0000000..e1fc983
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/Support.php
@@ -0,0 +1,460 @@
+
+ *
+ * @property string appid
+ * @property string app_id
+ * @property string miniapp_id
+ * @property string sub_appid
+ * @property string sub_app_id
+ * @property string sub_miniapp_id
+ * @property string mch_id
+ * @property string sub_mch_id
+ * @property string key
+ * @property string return_url
+ * @property string cert_client
+ * @property string cert_key
+ * @property array log
+ * @property array http
+ * @property string mode
+ */
+class Support
+{
+ use HasHttpRequest;
+
+ /**
+ * Wechat gateway.
+ *
+ * @var string
+ */
+ protected $baseUri;
+
+ /**
+ * Config.
+ *
+ * @var Config
+ */
+ protected $config;
+
+ /**
+ * Instance.
+ *
+ * @var Support
+ */
+ private static $instance;
+
+ /**
+ * Bootstrap.
+ *
+ * @author yansongda
+ */
+ private function __construct(Config $config)
+ {
+ $this->baseUri = Wechat::URL[$config->get('mode', Wechat::MODE_NORMAL)];
+ $this->config = $config;
+
+ $this->setHttpOptions();
+ }
+
+ /**
+ * __get.
+ *
+ * @author yansongda
+ *
+ * @param $key
+ *
+ * @return mixed|Config|null
+ */
+ public function __get($key)
+ {
+ return $this->getConfig($key);
+ }
+
+ /**
+ * create.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ *
+ * @return Support
+ */
+ public static function create(Config $config)
+ {
+ if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
+ self::$instance = new self($config);
+
+ self::setDevKey();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * getInstance.
+ *
+ * @author yansongda
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return Support
+ */
+ public static function getInstance()
+ {
+ if (is_null(self::$instance)) {
+ throw new InvalidArgumentException('You Should [Create] First Before Using');
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * clear.
+ *
+ * @author yansongda
+ */
+ public static function clear()
+ {
+ self::$instance = null;
+ }
+
+ /**
+ * Request wechat api.
+ *
+ * @author yansongda
+ *
+ * @param string $endpoint
+ * @param array $data
+ * @param bool $cert
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public static function requestApi($endpoint, $data, $cert = false): Collection
+ {
+ Events::dispatch(new Events\ApiRequesting('Wechat', '', self::$instance->getBaseUri().$endpoint, $data));
+
+ //xmlData需增加headers配置,否则微信方无法接收到参数
+ $options = [
+ 'headers' => [
+ 'Content-Type' => 'application/xml',
+ ],
+ ];
+
+ $certOptions = $cert ? [
+ 'cert' => self::$instance->cert_client,
+ 'ssl_key' => self::$instance->cert_key,
+ ] : [];
+
+ $result = self::$instance->post(
+ $endpoint,
+ self::toXml($data),
+ array_merge($options, $certOptions)
+ );
+ $result = is_array($result) ? $result : self::fromXml($result);
+
+ Events::dispatch(new Events\ApiRequested('Wechat', '', self::$instance->getBaseUri().$endpoint, $result));
+
+ return self::processingApiResult($endpoint, $result);
+ }
+
+ /**
+ * Filter payload.
+ *
+ * @author yansongda
+ *
+ * @param array $payload
+ * @param array|string $params
+ * @param bool $preserve_notify_url
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function filterPayload($payload, $params, $preserve_notify_url = false): array
+ {
+ $type = self::getTypeName($params['type'] ?? '');
+
+ $payload = array_merge(
+ $payload,
+ is_array($params) ? $params : ['out_trade_no' => $params]
+ );
+ $payload['appid'] = self::$instance->getConfig($type, '');
+
+ if (Wechat::MODE_SERVICE === self::$instance->getConfig('mode', Wechat::MODE_NORMAL)) {
+ $payload['sub_appid'] = self::$instance->getConfig('sub_'.$type, '');
+ }
+
+ unset($payload['trade_type'], $payload['type']);
+ if (!$preserve_notify_url) {
+ unset($payload['notify_url']);
+ }
+
+ $payload['sign'] = self::generateSign($payload);
+
+ return $payload;
+ }
+
+ /**
+ * Generate wechat sign.
+ *
+ * @author yansongda
+ *
+ * @param array $data
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function generateSign($data): string
+ {
+ $key = self::$instance->key;
+
+ if (is_null($key)) {
+ throw new InvalidArgumentException('Missing Wechat Config -- [key]');
+ }
+
+ ksort($data);
+
+ $string = md5(self::getSignContent($data).'&key='.$key);
+
+ Log::debug('Wechat Generate Sign Before UPPER', [$data, $string]);
+
+ return strtoupper($string);
+ }
+
+ /**
+ * Generate sign content.
+ *
+ * @author yansongda
+ *
+ * @param array $data
+ */
+ public static function getSignContent($data): string
+ {
+ $buff = '';
+
+ foreach ($data as $k => $v) {
+ $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+ }
+
+ Log::debug('Wechat Generate Sign Content Before Trim', [$data, $buff]);
+
+ return trim($buff, '&');
+ }
+
+ /**
+ * Decrypt refund contents.
+ *
+ * @author yansongda
+ *
+ * @param string $contents
+ */
+ public static function decryptRefundContents($contents): string
+ {
+ return openssl_decrypt(
+ base64_decode($contents),
+ 'AES-256-ECB',
+ md5(self::$instance->key),
+ OPENSSL_RAW_DATA
+ );
+ }
+
+ /**
+ * Convert array to xml.
+ *
+ * @author yansongda
+ *
+ * @param array $data
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function toXml($data): string
+ {
+ if (!is_array($data) || count($data) <= 0) {
+ throw new InvalidArgumentException('Convert To Xml Error! Invalid Array!');
+ }
+
+ $xml = '';
+ foreach ($data as $key => $val) {
+ $xml .= is_numeric($val) ? '<'.$key.'>'.$val.''.$key.'>' :
+ '<'.$key.'>'.$key.'>';
+ }
+ $xml .= ' ';
+
+ return $xml;
+ }
+
+ /**
+ * Convert xml to array.
+ *
+ * @author yansongda
+ *
+ * @param string $xml
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function fromXml($xml): array
+ {
+ if (!$xml) {
+ throw new InvalidArgumentException('Convert To Array Error! Invalid Xml!');
+ }
+
+ if (\PHP_VERSION_ID < 80000) {
+ libxml_disable_entity_loader(true);
+ }
+
+ return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
+ }
+
+ /**
+ * Get service config.
+ *
+ * @author yansongda
+ *
+ * @param string|null $key
+ * @param mixed|null $default
+ *
+ * @return mixed|null
+ */
+ public function getConfig($key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return $this->config->all();
+ }
+
+ if ($this->config->has($key)) {
+ return $this->config[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Get app id according to param type.
+ *
+ * @author yansongda
+ *
+ * @param string $type
+ */
+ public static function getTypeName($type = ''): string
+ {
+ switch ($type) {
+ case '':
+ $type = 'app_id';
+ break;
+ case 'app':
+ $type = 'appid';
+ break;
+ default:
+ $type = $type.'_id';
+ }
+
+ return $type;
+ }
+
+ /**
+ * Get Base Uri.
+ *
+ * @author yansongda
+ *
+ * @return string
+ */
+ public function getBaseUri()
+ {
+ return $this->baseUri;
+ }
+
+ /**
+ * processingApiResult.
+ *
+ * @author yansongda
+ *
+ * @param $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ *
+ * @return Collection
+ */
+ protected static function processingApiResult($endpoint, array $result)
+ {
+ if (!isset($result['return_code']) || 'SUCCESS' != $result['return_code']) {
+ throw new GatewayException('Get Wechat API Error:'.($result['return_msg'] ?? $result['retmsg'] ?? ''), $result);
+ }
+
+ if (isset($result['result_code']) && 'SUCCESS' != $result['result_code']) {
+ throw new BusinessException('Wechat Business Error: '.$result['err_code'].' - '.$result['err_code_des'], $result);
+ }
+
+ if (false !== strpos($endpoint, 'xdc/apiv2getsignkey') ||
+ false !== strpos($endpoint, 'mmpaymkttransfers') ||
+ self::generateSign($result) === $result['sign']) {
+ return new Collection($result);
+ }
+
+ Events::dispatch(new Events\SignFailed('Wechat', '', $result));
+
+ throw new InvalidSignException('Wechat Sign Verify FAILED', $result);
+ }
+
+ /**
+ * setDevKey.
+ *
+ * @author yansongda
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ * @throws Exception
+ *
+ * @return Support
+ */
+ private static function setDevKey()
+ {
+ if (Wechat::MODE_DEV == self::$instance->mode) {
+ $data = [
+ 'mch_id' => self::$instance->mch_id,
+ 'nonce_str' => Str::random(),
+ ];
+ $data['sign'] = self::generateSign($data);
+
+ $result = self::requestApi('https://api.mch.weixin.qq.com/xdc/apiv2getsignkey/sign/getsignkey', $data);
+
+ self::$instance->config->set('key', $result['sandbox_signkey']);
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Set Http options.
+ *
+ * @author yansongda
+ */
+ private function setHttpOptions(): self
+ {
+ if ($this->config->has('http') && is_array($this->config->get('http'))) {
+ $this->config->forget('http.base_uri');
+ $this->httpOptions = $this->config->get('http');
+ }
+
+ return $this;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/TransferGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/TransferGateway.php
new file mode 100644
index 0000000..bbd97b3
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/TransferGateway.php
@@ -0,0 +1,80 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): Collection
+ {
+ if (Wechat::MODE_SERVICE === $this->mode) {
+ unset($payload['sub_mch_id'], $payload['sub_appid']);
+ }
+
+ $type = Support::getTypeName($payload['type'] ?? '');
+
+ $payload['mch_appid'] = Support::getInstance()->getConfig($type, '');
+ $payload['mchid'] = $payload['mch_id'];
+
+ if ('cli' !== php_sapi_name() && !isset($payload['spbill_create_ip'])) {
+ $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+ }
+
+ unset($payload['appid'], $payload['mch_id'], $payload['trade_type'],
+ $payload['notify_url'], $payload['type']);
+
+ $payload['sign'] = Support::generateSign($payload);
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Transfer', $endpoint, $payload));
+
+ return Support::requestApi(
+ 'mmpaymkttransfers/promotion/transfers',
+ $payload,
+ true
+ );
+ }
+
+ /**
+ * Find.
+ *
+ * @author yansongda
+ *
+ * @param $order
+ */
+ public function find($order): array
+ {
+ return [
+ 'endpoint' => 'mmpaymkttransfers/gettransferinfo',
+ 'order' => is_array($order) ? $order : ['partner_trade_no' => $order],
+ 'cert' => true,
+ ];
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return '';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WapGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WapGateway.php
new file mode 100644
index 0000000..0c0c920
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WapGateway.php
@@ -0,0 +1,47 @@
+
+ *
+ * @param string $endpoint
+ *
+ * @throws GatewayException
+ * @throws InvalidArgumentException
+ * @throws InvalidSignException
+ */
+ public function pay($endpoint, array $payload): RedirectResponse
+ {
+ $payload['trade_type'] = $this->getTradeType();
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Wap', $endpoint, $payload));
+
+ $mweb_url = $this->preOrder($payload)->get('mweb_url');
+
+ $url = is_null(Support::getInstance()->return_url) ? $mweb_url : $mweb_url.
+ '&redirect_url='.urlencode(Support::getInstance()->return_url);
+
+ return new RedirectResponse($url);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @author yansongda
+ */
+ protected function getTradeType(): string
+ {
+ return 'MWEB';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WebGateway.php b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WebGateway.php
new file mode 100644
index 0000000..9fe0978
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Gateways/Wechat/WebGateway.php
@@ -0,0 +1,86 @@
+
+ *
+ */
+ public function pay($endpoint, array $payload): Response
+ {
+ $payload['spbill_create_ip'] = Request::createFromGlobals()->server->get('SERVER_ADDR');
+ $payload['trade_type'] = $this->getTradeType();
+
+ $code_url = $this->preOrder($payload)['code_url'];
+ $params = [
+ 'body' => $payload['body'],
+ 'code_url' => $code_url,
+ 'out_trade_no' => $payload['out_trade_no'],
+ 'return_url' => Support::getInstance()->return_url,
+ 'total_fee' => $payload['total_fee'],
+ ];
+
+ $params['sign'] = md5(implode('', $params) . Support::getInstance()->app_id);
+ $endpoint = addon_url("epay/api/wechat");
+
+ Events::dispatch(new Events\PayStarted('Wechat', 'Web/Wap', $endpoint, $payload));
+
+ return $this->buildPayHtml($endpoint, $params);
+ }
+
+ /**
+ * Build Html response.
+ *
+ * @param string $endpoint
+ * @param array $payload
+ * @param string $method
+ *
+ * @return Response
+ * @author yansongda
+ *
+ */
+ protected function buildPayHtml($endpoint, $payload, $method = 'POST'): Response
+ {
+ if (strtoupper($method) === 'GET') {
+ return new RedirectResponse($endpoint . '?' . http_build_query($payload));
+ }
+
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response($sHtml);
+ }
+
+ /**
+ * Get trade type config.
+ *
+ * @return string
+ * @author yansongda
+ *
+ */
+ protected function getTradeType(): string
+ {
+ return 'NATIVE';
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/LICENSE b/server/addons/epay/library/v2/Yansongda/Pay/LICENSE
new file mode 100644
index 0000000..6ceb153
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Listeners/KernelLogSubscriber.php b/server/addons/epay/library/v2/Yansongda/Pay/Listeners/KernelLogSubscriber.php
new file mode 100644
index 0000000..2250de3
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Listeners/KernelLogSubscriber.php
@@ -0,0 +1,114 @@
+ 'methodName')
+ * * array('eventName' => array('methodName', $priority))
+ * * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
+ *
+ * @return array The event names to listen to
+ */
+ public static function getSubscribedEvents()
+ {
+ return [
+ Events\PayStarting::class => ['writePayStartingLog', 256],
+ Events\PayStarted::class => ['writePayStartedLog', 256],
+ Events\ApiRequesting::class => ['writeApiRequestingLog', 256],
+ Events\ApiRequested::class => ['writeApiRequestedLog', 256],
+ Events\SignFailed::class => ['writeSignFailedLog', 256],
+ Events\RequestReceived::class => ['writeRequestReceivedLog', 256],
+ Events\MethodCalled::class => ['writeMethodCalledLog', 256],
+ ];
+ }
+
+ /**
+ * writePayStartingLog.
+ *
+ * @author yansongda
+ */
+ public function writePayStartingLog(Events\PayStarting $event)
+ {
+ Log::debug("Starting To {$event->driver}", [$event->gateway, $event->params]);
+ }
+
+ /**
+ * writePayStartedLog.
+ *
+ * @author yansongda
+ */
+ public function writePayStartedLog(Events\PayStarted $event)
+ {
+ Log::info(
+ "{$event->driver} {$event->gateway} Has Started",
+ [$event->endpoint, $event->payload]
+ );
+ }
+
+ /**
+ * writeApiRequestingLog.
+ *
+ * @author yansongda
+ */
+ public function writeApiRequestingLog(Events\ApiRequesting $event)
+ {
+ Log::debug("Requesting To {$event->driver} Api", [$event->endpoint, $event->payload]);
+ }
+
+ /**
+ * writeApiRequestedLog.
+ *
+ * @author yansongda
+ */
+ public function writeApiRequestedLog(Events\ApiRequested $event)
+ {
+ Log::debug("Result Of {$event->driver} Api", $event->result);
+ }
+
+ /**
+ * writeSignFailedLog.
+ *
+ * @author yansongda
+ */
+ public function writeSignFailedLog(Events\SignFailed $event)
+ {
+ Log::warning("{$event->driver} Sign Verify FAILED", $event->data);
+ }
+
+ /**
+ * writeRequestReceivedLog.
+ *
+ * @author yansongda
+ */
+ public function writeRequestReceivedLog(Events\RequestReceived $event)
+ {
+ Log::info("Received {$event->driver} Request", $event->data);
+ }
+
+ /**
+ * writeMethodCalledLog.
+ *
+ * @author yansongda
+ */
+ public function writeMethodCalledLog(Events\MethodCalled $event)
+ {
+ Log::info("{$event->driver} {$event->gateway} Method Has Called", [$event->endpoint, $event->payload]);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Log.php b/server/addons/epay/library/v2/Yansongda/Pay/Log.php
new file mode 100644
index 0000000..c1f8c3e
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Log.php
@@ -0,0 +1,49 @@
+
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $args)
+ {
+ return forward_static_call_array([BaseLog::class, $method], $args);
+ }
+
+ /**
+ * Forward call.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @return mixed
+ */
+ public function __call($method, $args)
+ {
+ return call_user_func_array([BaseLog::class, $method], $args);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Pay/Pay.php b/server/addons/epay/library/v2/Yansongda/Pay/Pay.php
new file mode 100644
index 0000000..eb33e3d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Pay/Pay.php
@@ -0,0 +1,131 @@
+
+ *
+ * @throws Exception
+ */
+ public function __construct(array $config)
+ {
+ $this->config = new Config($config);
+
+ $this->registerLogService();
+ $this->registerEventService();
+ }
+
+ /**
+ * Magic static call.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $params
+ *
+ * @throws InvalidGatewayException
+ * @throws Exception
+ */
+ public static function __callStatic($method, $params): GatewayApplicationInterface
+ {
+ $app = new self(...$params);
+
+ return $app->create($method);
+ }
+
+ /**
+ * Create a instance.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ *
+ * @throws InvalidGatewayException
+ */
+ protected function create($method): GatewayApplicationInterface
+ {
+ $gateway = __NAMESPACE__.'\\Gateways\\'.Str::studly($method);
+
+ if (class_exists($gateway)) {
+ return self::make($gateway);
+ }
+
+ throw new InvalidGatewayException("Gateway [{$method}] Not Exists");
+ }
+
+ /**
+ * Make a gateway.
+ *
+ * @author yansongda
+ *
+ * @param string $gateway
+ *
+ * @throws InvalidGatewayException
+ */
+ protected function make($gateway): GatewayApplicationInterface
+ {
+ $app = new $gateway($this->config);
+
+ if ($app instanceof GatewayApplicationInterface) {
+ return $app;
+ }
+
+ throw new InvalidGatewayException("Gateway [{$gateway}] Must Be An Instance Of GatewayApplicationInterface");
+ }
+
+ /**
+ * Register log service.
+ *
+ * @author yansongda
+ *
+ * @throws Exception
+ */
+ protected function registerLogService()
+ {
+ $config = $this->config->get('log');
+ $config['identify'] = 'yansongda.pay';
+
+ $logger = new Logger();
+ $logger->setConfig($config);
+
+ Log::setInstance($logger);
+ }
+
+ /**
+ * Register event service.
+ *
+ * @author yansongda
+ */
+ protected function registerEventService()
+ {
+ Events::setDispatcher(Events::createDispatcher());
+
+ Events::addSubscriber(new KernelLogSubscriber());
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Arr.php b/server/addons/epay/library/v2/Yansongda/Supports/Arr.php
new file mode 100644
index 0000000..f247da8
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Arr.php
@@ -0,0 +1,605 @@
+ $value) {
+ [$innerKey, $innerValue] = call_user_func($callback, $key, $value);
+ $results[$innerKey] = $innerValue;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Divide an array into two arrays. One with keys and the other with values.
+ */
+ public static function divide(array $array): array
+ {
+ return [
+ array_keys($array),
+ array_values($array),
+ ];
+ }
+
+ /**
+ * Flatten a multi-dimensional associative array with dots.
+ */
+ public static function dot(array $array, string $prepend = ''): array
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+ } else {
+ $results[$prepend.$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Get all of the given array except for a specified array of items.
+ *
+ * @param array|string $keys
+ */
+ public static function except(array $array, $keys): array
+ {
+ return array_diff_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * access array.
+ *
+ * if not array access, return original.
+ *
+ * @author yansongda
+ *
+ * @param mixed $data
+ *
+ * @return mixed
+ */
+ public static function access($data)
+ {
+ if (!self::accessible($data) &&
+ !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ return is_object($data) ? $data->toArray() : $data;
+ }
+
+ /**
+ * Determine if the given key exists in the provided array.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|int $key
+ *
+ * @return bool
+ */
+ public static function exists($array, $key)
+ {
+ $array = self::access($array);
+
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Check if an item or items exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ *
+ * @return bool
+ */
+ public static function has($array, $keys)
+ {
+ $array = self::access($array);
+
+ $keys = (array) $keys;
+
+ if (!$array || $keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+
+ if (static::exists($array, $key)) {
+ continue;
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if any of the keys exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ *
+ * @return bool
+ */
+ public static function hasAny($array, $keys)
+ {
+ $array = self::access($array);
+
+ if (is_null($keys)) {
+ return false;
+ }
+
+ $keys = (array) $keys;
+
+ if (!$array) {
+ return false;
+ }
+
+ if ($keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ if (static::has($array, $key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Fetch a flattened array of a nested array element.
+ */
+ public static function fetch(array $array, string $key): array
+ {
+ $results = [];
+
+ foreach (explode('.', $key) as $segment) {
+ $results = [];
+ foreach ($array as $value) {
+ $value = (array) $value;
+ $results[] = $value[$segment];
+ }
+ $array = array_values($results);
+ }
+
+ return array_values($results);
+ }
+
+ /**
+ * Return the first element in an array passing a given truth test.
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public static function first(array $array, callable $callback, $default = null)
+ {
+ foreach ($array as $key => $value) {
+ if (call_user_func($callback, $key, $value)) {
+ return $value;
+ }
+ }
+
+ return $default;
+ }
+
+ /**
+ * Return the last element in an array passing a given truth test.
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public static function last(array $array, callable $callback, $default = null)
+ {
+ return static::first(array_reverse($array), $callback, $default);
+ }
+
+ /**
+ * Flatten a multi-dimensional array into a single level.
+ */
+ public static function flatten(array $array): array
+ {
+ $return = [];
+ array_walk_recursive(
+ $array,
+ function ($x) use (&$return) {
+ $return[] = $x;
+ }
+ );
+
+ return $return;
+ }
+
+ /**
+ * Remove one or many array items from a given array using "dot" notation.
+ *
+ * @param array $array
+ * @param array|string $keys
+ */
+ public static function forget(&$array, $keys)
+ {
+ $original = &$array;
+
+ $keys = (array) $keys;
+
+ if (0 === count($keys)) {
+ return;
+ }
+
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+
+ continue;
+ }
+
+ $parts = explode('.', $key);
+
+ // clean up before each pass
+ $array = &$original;
+
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+
+ if (isset($array[$part]) && is_array($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ /**
+ * Get an item from an array using "dot" notation.
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public static function get(array $array, string $key, $default = null)
+ {
+ if (is_null($key)) {
+ return $array;
+ }
+
+ if (isset($array[$key])) {
+ return $array[$key];
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (!is_array($array) || !array_key_exists($segment, $array)) {
+ return $default;
+ }
+ $array = $array[$segment];
+ }
+
+ return $array;
+ }
+
+ /**
+ * Get a subset of the items from the given array.
+ *
+ * @param array|string $keys
+ */
+ public static function only(array $array, $keys): array
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * Pluck an array of values from an array.
+ *
+ * @param string $key
+ */
+ public static function pluck(array $array, string $value, ?string $key = null): array
+ {
+ $results = [];
+
+ foreach ($array as $item) {
+ $itemValue = is_object($item) ? $item->{$value} : $item[$value];
+ // If the key is "null", we will just append the value to the array and keep
+ // looping. Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = is_object($item) ? $item->{$key} : $item[$key];
+ $results[$itemKey] = $itemValue;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Push an item onto the beginning of an array.
+ *
+ * @param mixed $value
+ * @param mixed $key
+ *
+ * @return array
+ */
+ public static function prepend(array $array, $value, $key = null)
+ {
+ if (is_null($key)) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Get a value from the array, and remove it.
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public static function pull(array &$array, string $key, $default = null)
+ {
+ $value = static::get($array, $key, $default);
+
+ static::forget($array, $key);
+
+ return $value;
+ }
+
+ /**
+ * Get one or a specified number of random values from an array.
+ *
+ * @param array $array
+ * @param int|null $number
+ *
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function random(array $array, $number = null)
+ {
+ $requested = is_null($number) ? 1 : $number;
+
+ $count = count($array);
+
+ $number = $requested > $count ? $count : $requested;
+
+ if (is_null($number)) {
+ return $array[array_rand($array)];
+ }
+
+ if (0 === (int) $number) {
+ return [];
+ }
+
+ $keys = array_rand($array, $number);
+
+ $results = [];
+
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ *
+ * If no key is given to the method, the entire array will be replaced.
+ *
+ * @param mixed $value
+ */
+ public static function set(array &$array, string $key, $value): array
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+
+ $keys = explode('.', $key);
+
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (!isset($array[$key]) || !is_array($array[$key])) {
+ $array[$key] = [];
+ }
+ $array = &$array[$key];
+ }
+ $array[array_shift($keys)] = $value;
+
+ return $array;
+ }
+
+ /**
+ * Sort the array using the given Closure.
+ */
+ public static function sort(array $array, callable $callback): array
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ $results[$key] = $callback($value);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Shuffle the given array and return the result.
+ *
+ * @param array $array
+ * @param int|null $seed
+ *
+ * @return array
+ */
+ public static function shuffle(array $array, $seed = null): array
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ mt_srand($seed);
+ shuffle($array);
+ mt_srand();
+ }
+
+ return $array;
+ }
+
+ /**
+ * Convert the array into a query string.
+ */
+ public static function query(array $array): string
+ {
+ return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
+ }
+
+ /**
+ * Filter the array using the given callback.
+ */
+ public static function where(array $array, ?callable $callback = null): array
+ {
+ return array_filter($array, $callback ?? function ($value) use ($callback) {
+ if (static::accessible($value)) {
+ $value = static::where($value, $callback);
+ }
+
+ if (is_array($value) && 0 === count($value)) {
+ $value = null;
+ }
+
+ return '' !== $value && !is_null($value);
+ }, ARRAY_FILTER_USE_BOTH);
+ }
+
+ /**
+ * Convert encoding.
+ *
+ * @author yansongda
+ *
+ * @param string $from_encoding
+ */
+ public static function encoding(array $array, string $to_encoding, $from_encoding = 'gb2312'): array
+ {
+ $encoded = [];
+
+ foreach ($array as $key => $value) {
+ $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) :
+ mb_convert_encoding($value, $to_encoding, $from_encoding);
+ }
+
+ return $encoded;
+ }
+
+ /**
+ * camelCaseKey.
+ *
+ * @author yansongda
+ *
+ * @param mixed $data
+ *
+ * @return mixed
+ */
+ public static function camelCaseKey($data)
+ {
+ if (!self::accessible($data) &&
+ !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $result = [];
+ $data = self::access($data);
+
+ foreach ($data as $key => $value) {
+ $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * snakeCaseKey.
+ *
+ * @author yansongda
+ *
+ * @param mixed $data
+ *
+ * @return mixed
+ */
+ public static function snakeCaseKey($data)
+ {
+ if (!self::accessible($data) &&
+ !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $data = self::access($data);
+ $result = [];
+
+ foreach ($data as $key => $value) {
+ $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value);
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Collection.php b/server/addons/epay/library/v2/Yansongda/Supports/Collection.php
new file mode 100644
index 0000000..b5ceaa5
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Collection.php
@@ -0,0 +1,386 @@
+ $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * To string.
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * Get a data by key.
+ *
+ * @return mixed
+ */
+ public function __get(string $key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Assigns a value to the specified data.
+ *
+ * @param mixed $value
+ */
+ public function __set(string $key, $value)
+ {
+ $this->set($key, $value);
+ }
+
+ /**
+ * Whether or not an data exists by key.
+ */
+ public function __isset(string $key): bool
+ {
+ return $this->has($key);
+ }
+
+ /**
+ * Unsets an data by key.
+ */
+ public function __unset(string $key)
+ {
+ $this->forget($key);
+ }
+
+ /**
+ * Return all items.
+ */
+ public function all(): array
+ {
+ return $this->items;
+ }
+
+ /**
+ * Return specific items.
+ */
+ public function only(array $keys): array
+ {
+ $return = [];
+
+ foreach ($keys as $key) {
+ $value = $this->get($key);
+
+ if (!is_null($value)) {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param mixed $keys
+ *
+ * @return static
+ */
+ public function except($keys)
+ {
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ /**
+ * Merge data.
+ *
+ * @param Collection|array $items
+ */
+ public function merge($items): array
+ {
+ foreach ($items as $key => $value) {
+ $this->set($key, $value);
+ }
+
+ return $this->all();
+ }
+
+ /**
+ * To determine Whether the specified element exists.
+ */
+ public function has(string $key): bool
+ {
+ return !is_null(Arr::get($this->items, $key));
+ }
+
+ /**
+ * Retrieve the first item.
+ *
+ * @return mixed
+ */
+ public function first()
+ {
+ return reset($this->items);
+ }
+
+ /**
+ * Retrieve the last item.
+ *
+ * @return mixed
+ */
+ public function last()
+ {
+ $end = end($this->items);
+
+ reset($this->items);
+
+ return $end;
+ }
+
+ /**
+ * add the item value.
+ *
+ * @param mixed $value
+ */
+ public function add(string $key, $value)
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ /**
+ * Set the item value.
+ *
+ * @param mixed $value
+ */
+ public function set(string $key, $value)
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ /**
+ * Retrieve item from Collection.
+ *
+ * @param string|null $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get(?string $key = null, $default = null)
+ {
+ return Arr::get($this->items, $key, $default);
+ }
+
+ /**
+ * Remove item form Collection.
+ */
+ public function forget(string $key)
+ {
+ Arr::forget($this->items, $key);
+ }
+
+ /**
+ * Build to array.
+ */
+ public function toArray(): array
+ {
+ return $this->all();
+ }
+
+ /**
+ * Build to json.
+ */
+ public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+ {
+ return json_encode($this->all(), $option);
+ }
+
+ /**
+ * (PHP 5 >= 5.4.0)
+ * Specify data which should be serialized to JSON.
+ *
+ * @see http://php.net/manual/en/jsonserializable.jsonserialize.php
+ *
+ * @return mixed data which can be serialized by json_encode ,
+ * which is a value of any type other than a resource
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->items;
+ }
+
+ /**
+ * (PHP 5 >= 5.1.0)
+ * String representation of object.
+ *
+ * @see http://php.net/manual/en/serializable.serialize.php
+ *
+ * @return string the string representation of the object or null
+ */
+ public function serialize()
+ {
+ return serialize($this->items);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Retrieve an external iterator.
+ *
+ * @see http://php.net/manual/en/iteratoraggregate.getiterator.php
+ *
+ * @return ArrayIterator An instance of an object implementing Iterator or
+ * ArrayIterator
+ */
+ public function getIterator(): \Traversable
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ /**
+ * (PHP 5 >= 5.1.0)
+ * Count elements of an object.
+ *
+ * @see http://php.net/manual/en/countable.count.php
+ *
+ * @return int The custom count as an integer.
+ *
+ *
+ * The return value is cast to an integer
+ */
+ public function count(): int
+ {
+ return count($this->items);
+ }
+
+ /**
+ * (PHP 5 >= 5.1.0)
+ * Constructs the object.
+ *
+ * @see http://php.net/manual/en/serializable.unserialize.php
+ *
+ * @param string $data
+ * The string representation of the object.
+ *
+ *
+ * @return mixed|void
+ */
+ public function unserialize($data)
+ {
+ return $this->items = unserialize($data);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Whether a offset exists.
+ *
+ * @see http://php.net/manual/en/arrayaccess.offsetexists.php
+ *
+ * @param mixed $offset
+ * An offset to check for.
+ *
+ *
+ * @return bool true on success or false on failure.
+ * The return value will be casted to boolean if non-boolean was returned
+ */
+ public function offsetExists($offset): bool
+ {
+ return $this->has($offset);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Offset to unset.
+ *
+ * @see http://php.net/manual/en/arrayaccess.offsetunset.php
+ *
+ * @param mixed $offset
+ * The offset to unset.
+ *
+ */
+ public function offsetUnset($offset): void
+ {
+ if ($this->offsetExists($offset)) {
+ $this->forget($offset);
+ }
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Offset to retrieve.
+ *
+ * @see http://php.net/manual/en/arrayaccess.offsetget.php
+ *
+ * @param mixed $offset
+ * The offset to retrieve.
+ *
+ *
+ * @return mixed Can return all value types
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->offsetExists($offset) ? $this->get($offset) : null;
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Offset to set.
+ *
+ * @see http://php.net/manual/en/arrayaccess.offsetset.php
+ *
+ * @param mixed $offset
+ * The offset to assign the value to.
+ *
+ * @param mixed $value
+ * The value to set.
+ *
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value)
+ {
+ $this->set($offset, $value);
+ }
+
+ /**
+ * (PHP 8.1+) Magic method for serialization.
+ *
+ * @return array
+ */
+ public function __serialize(): array
+ {
+ return $this->items;
+ }
+
+ /**
+ * (PHP 8.1+) Magic method for unserialization.
+ *
+ * @param array $data
+ */
+ public function __unserialize(array $data): void
+ {
+ $this->items = $data;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Config.php b/server/addons/epay/library/v2/Yansongda/Supports/Config.php
new file mode 100644
index 0000000..a660bad
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Config.php
@@ -0,0 +1,7 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Log.php b/server/addons/epay/library/v2/Yansongda/Supports/Log.php
new file mode 100644
index 0000000..4901a2d
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Log.php
@@ -0,0 +1,91 @@
+
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @throws \Exception
+ */
+ public function __call($method, $args): void
+ {
+ call_user_func_array([self::getInstance(), $method], $args);
+ }
+
+ /**
+ * __callStatic.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @throws \Exception
+ */
+ public static function __callStatic($method, $args): void
+ {
+ forward_static_call_array([self::getInstance(), $method], $args);
+ }
+
+ /**
+ * getInstance.
+ *
+ * @author yansongda
+ *
+ * @return \Yansongda\Supports\Logger
+ */
+ public static function getInstance(): Logger
+ {
+ if (is_null(self::$instance)) {
+ self::$instance = new Logger();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * setInstance.
+ *
+ * @author yansongda
+ *
+ * @param \Yansongda\Supports\Logger $logger
+ *
+ * @throws \Exception
+ */
+ public static function setInstance(Logger $logger): void
+ {
+ self::$instance = $logger;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Logger.php b/server/addons/epay/library/v2/Yansongda/Supports/Logger.php
new file mode 100644
index 0000000..7232c59
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Logger.php
@@ -0,0 +1,240 @@
+ null,
+ 'identify' => 'yansongda.supports',
+ 'level' => BaseLogger::DEBUG,
+ 'type' => 'daily',
+ 'max_files' => 30,
+ ];
+
+ /**
+ * Forward call.
+ *
+ * @author yansongda
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @throws Exception
+ */
+ public function __call($method, $args): void
+ {
+ call_user_func_array([$this->getLogger(), $method], $args);
+ }
+
+ /**
+ * Set logger.
+ *
+ * @author yansongda
+ */
+ public function setLogger(LoggerInterface $logger): Logger
+ {
+ $this->logger = $logger;
+
+ return $this;
+ }
+
+ /**
+ * Return the logger instance.
+ *
+ * @author yansongda
+ *
+ * @throws Exception
+ */
+ public function getLogger(): LoggerInterface
+ {
+ if (is_null($this->logger)) {
+ $this->logger = $this->createLogger();
+ }
+
+ return $this->logger;
+ }
+
+ /**
+ * Make a default log instance.
+ *
+ * @author yansongda
+ *
+ * @throws Exception
+ */
+ public function createLogger(): BaseLogger
+ {
+ $handler = $this->getHandler();
+
+ $handler->setFormatter($this->getFormatter());
+
+ $logger = new BaseLogger($this->config['identify']);
+
+ $logger->pushHandler($handler);
+
+ return $logger;
+ }
+
+ /**
+ * setFormatter.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setFormatter(FormatterInterface $formatter): self
+ {
+ $this->formatter = $formatter;
+
+ return $this;
+ }
+
+ /**
+ * getFormatter.
+ *
+ * @author yansongda
+ */
+ public function getFormatter(): FormatterInterface
+ {
+ if (is_null($this->formatter)) {
+ $this->formatter = $this->createFormatter();
+ }
+
+ return $this->formatter;
+ }
+
+ /**
+ * createFormatter.
+ *
+ * @author yansongda
+ */
+ public function createFormatter(): LineFormatter
+ {
+ return new LineFormatter(
+ "%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n",
+ null,
+ false,
+ true
+ );
+ }
+
+ /**
+ * setHandler.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setHandler(AbstractHandler $handler): self
+ {
+ $this->handler = $handler;
+
+ return $this;
+ }
+
+ /**
+ * getHandler.
+ *
+ * @author yansongda
+ *
+ * @throws \Exception
+ */
+ public function getHandler(): AbstractHandler
+ {
+ if (is_null($this->handler)) {
+ $this->handler = $this->createHandler();
+ }
+
+ return $this->handler;
+ }
+
+ /**
+ * createHandler.
+ *
+ * @author yansongda
+ *
+ * @throws \Exception
+ *
+ * @return \Monolog\Handler\RotatingFileHandler|\Monolog\Handler\StreamHandler
+ */
+ public function createHandler(): AbstractHandler
+ {
+ $file = $this->config['file'] ?? sys_get_temp_dir().'/logs/'.$this->config['identify'].'.log';
+
+ if ('single' === $this->config['type']) {
+ return new StreamHandler($file, $this->config['level']);
+ }
+
+ return new RotatingFileHandler($file, $this->config['max_files'], $this->config['level']);
+ }
+
+ /**
+ * setConfig.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setConfig(array $config): self
+ {
+ $this->config = array_merge($this->config, $config);
+
+ return $this;
+ }
+
+ /**
+ * getConfig.
+ *
+ * @author yansongda
+ */
+ public function getConfig(): array
+ {
+ return $this->config;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Logger/StdoutHandler.php b/server/addons/epay/library/v2/Yansongda/Supports/Logger/StdoutHandler.php
new file mode 100644
index 0000000..bba384f
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Logger/StdoutHandler.php
@@ -0,0 +1,36 @@
+output = $output ?? new ConsoleOutput();
+ parent::__construct($level, $bubble);
+ }
+
+ /**
+ * Writes the record down to the log of the implementing handler.
+ */
+ protected function write(array $record): void
+ {
+ $this->output->writeln($record['formatted']);
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Str.php b/server/addons/epay/library/v2/Yansongda/Supports/Str.php
new file mode 100644
index 0000000..b540be6
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Str.php
@@ -0,0 +1,570 @@
+ $val) {
+ $value = str_replace($val, $key, $value);
+ }
+
+ return preg_replace('/[^\x20-\x7E]/u', '', $value);
+ }
+
+ /**
+ * Get the portion of a string before a given value.
+ */
+ public static function before(string $subject, string $search): string
+ {
+ return '' === $search ? $subject : explode($search, $subject)[0];
+ }
+
+ /**
+ * Convert a value to camel case.
+ */
+ public static function camel(string $value): string
+ {
+ if (isset(static::$camelCache[$value])) {
+ return static::$camelCache[$value];
+ }
+
+ return static::$camelCache[$value] = lcfirst(static::studly($value));
+ }
+
+ /**
+ * Determine if a given string contains a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function contains(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if ('' !== $needle && false !== mb_strpos($haystack, $needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if a given string ends with a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function endsWith(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if (substr($haystack, -strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Cap a string with a single instance of a given value.
+ */
+ public static function finish(string $value, string $cap): string
+ {
+ $quoted = preg_quote($cap, '/');
+
+ return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
+ }
+
+ /**
+ * Determine if a given string matches a given pattern.
+ *
+ * @param string|array $pattern
+ */
+ public static function is($pattern, string $value): bool
+ {
+ $patterns = is_array($pattern) ? $pattern : (array) $pattern;
+
+ if (empty($patterns)) {
+ return false;
+ }
+
+ foreach ($patterns as $pattern) {
+ // If the given value is an exact match we can of course return true right
+ // from the beginning. Otherwise, we will translate asterisks and do an
+ // actual pattern match against the two strings to see if they match.
+ if ($pattern == $value) {
+ return true;
+ }
+
+ $pattern = preg_quote($pattern, '#');
+
+ // Asterisks are translated into zero-or-more regular expression wildcards
+ // to make it convenient to check if the strings starts with the given
+ // pattern such as "library/*", making any string check convenient.
+ $pattern = str_replace('\*', '.*', $pattern);
+
+ if (1 === preg_match('#^'.$pattern.'\z#u', $value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a string to kebab case.
+ */
+ public static function kebab(string $value): string
+ {
+ return static::snake($value, '-');
+ }
+
+ /**
+ * Return the length of the given string.
+ *
+ * @param string $encoding
+ */
+ public static function length(string $value, ?string $encoding = null): int
+ {
+ if (null !== $encoding) {
+ return mb_strlen($value, $encoding);
+ }
+
+ return mb_strlen($value);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ */
+ public static function limit(string $value, int $limit = 100, string $end = '...'): string
+ {
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
+ return $value;
+ }
+
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ */
+ public static function lower(string $value): string
+ {
+ return mb_strtolower($value, 'UTF-8');
+ }
+
+ /**
+ * Limit the number of words in a string.
+ */
+ public static function words(string $value, int $words = 100, string $end = '...'): string
+ {
+ preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
+
+ if (!isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+ return $value;
+ }
+
+ return rtrim($matches[0]).$end;
+ }
+
+ /**
+ * Parse a Class.
+ */
+ public static function parseCallback(string $callback, ?string $default = null): array
+ {
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+ }
+
+ /**
+ * Generate a more truly "random" alpha-numeric string.
+ *
+ * @throws Exception
+ */
+ public static function random(int $length = 16): string
+ {
+ $string = '';
+
+ while (($len = strlen($string)) < $length) {
+ $size = $length - $len;
+
+ $bytes = function_exists('random_bytes') ? random_bytes($size) : mt_rand();
+
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ */
+ public static function replaceArray(string $search, array $replace, string $subject): string
+ {
+ foreach ($replace as $value) {
+ $subject = static::replaceFirst($search, $value, $subject);
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ */
+ public static function replaceFirst(string $search, string $replace, string $subject): string
+ {
+ if ('' == $search) {
+ return $subject;
+ }
+
+ $position = strpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ */
+ public static function replaceLast(string $search, string $replace, string $subject): string
+ {
+ $position = strrpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ */
+ public static function start(string $value, string $prefix): string
+ {
+ $quoted = preg_quote($prefix, '/');
+
+ return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ */
+ public static function upper(string $value): string
+ {
+ return mb_strtoupper($value, 'UTF-8');
+ }
+
+ /**
+ * Convert the given string to title case.
+ */
+ public static function title(string $value): string
+ {
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ */
+ public static function slug(string $title, string $separator = '-', string $language = 'en'): string
+ {
+ $title = static::ascii($title, $language);
+
+ // Convert all dashes/underscores into separator
+ $flip = '-' == $separator ? '_' : '-';
+
+ $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+ // Replace @ with the word 'at'
+ $title = str_replace('@', $separator.'at'.$separator, $title);
+
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
+ $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+ return trim($title, $separator);
+ }
+
+ /**
+ * Convert a string to snake case.
+ */
+ public static function snake(string $value, string $delimiter = '_'): string
+ {
+ $key = $value;
+
+ if (isset(static::$snakeCache[$key][$delimiter])) {
+ return static::$snakeCache[$key][$delimiter];
+ }
+
+ if (!ctype_lower($value)) {
+ $value = preg_replace('/\s+/u', '', ucwords($value));
+
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+ }
+
+ return static::$snakeCache[$key][$delimiter] = $value;
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function startsWith(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if ('' !== $needle && substr($haystack, 0, strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ */
+ public static function studly(string $value): string
+ {
+ $key = $value;
+
+ if (isset(static::$studlyCache[$key])) {
+ return static::$studlyCache[$key];
+ }
+
+ $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+ return static::$studlyCache[$key] = str_replace(' ', '', $value);
+ }
+
+ /**
+ * Returns the portion of string specified by the start and length parameters.
+ */
+ public static function substr(string $string, int $start, ?int $length = null): string
+ {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ */
+ public static function ucfirst(string $string): string
+ {
+ return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+ }
+
+ /**
+ * Convert string's encoding.
+ *
+ * @author yansongda
+ */
+ public static function encoding(string $string, string $to = 'utf-8', string $from = 'gb2312'): string
+ {
+ return mb_convert_encoding($string, $to, $from);
+ }
+
+ /**
+ * Returns the replacements for the ascii method.
+ *
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function charsArray(): array
+ {
+ static $charsArray;
+
+ if (isset($charsArray)) {
+ return $charsArray;
+ }
+
+ return $charsArray = [
+ '0' => ['°', '₀', '۰', '0'],
+ '1' => ['¹', '₁', '۱', '1'],
+ '2' => ['²', '₂', '۲', '2'],
+ '3' => ['³', '₃', '۳', '3'],
+ '4' => ['⁴', '₄', '۴', '٤', '4'],
+ '5' => ['⁵', '₅', '۵', '٥', '5'],
+ '6' => ['⁶', '₆', '۶', '٦', '6'],
+ '7' => ['⁷', '₇', '۷', '7'],
+ '8' => ['⁸', '₈', '۸', '8'],
+ '9' => ['⁹', '₉', '۹', '9'],
+ 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'],
+ 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
+ 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
+ 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
+ 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
+ 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
+ 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
+ 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
+ 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i'],
+ 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
+ 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
+ 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
+ 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
+ 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
+ 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
+ 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
+ 'q' => ['ყ', 'q'],
+ 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
+ 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
+ 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
+ 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
+ 'v' => ['в', 'ვ', 'ϐ', 'v'],
+ 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
+ 'x' => ['χ', 'ξ', 'x'],
+ 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
+ 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
+ 'aa' => ['ع', 'आ', 'آ'],
+ 'ae' => ['æ', 'ǽ'],
+ 'ai' => ['ऐ'],
+ 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
+ 'dj' => ['ђ', 'đ'],
+ 'dz' => ['џ', 'ძ'],
+ 'ei' => ['ऍ'],
+ 'gh' => ['غ', 'ღ'],
+ 'ii' => ['ई'],
+ 'ij' => ['ij'],
+ 'kh' => ['х', 'خ', 'ხ'],
+ 'lj' => ['љ'],
+ 'nj' => ['њ'],
+ 'oe' => ['ö', 'œ', 'ؤ'],
+ 'oi' => ['ऑ'],
+ 'oii' => ['ऒ'],
+ 'ps' => ['ψ'],
+ 'sh' => ['ш', 'შ', 'ش'],
+ 'shch' => ['щ'],
+ 'ss' => ['ß'],
+ 'sx' => ['ŝ'],
+ 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
+ 'ts' => ['ц', 'ც', 'წ'],
+ 'ue' => ['ü'],
+ 'uu' => ['ऊ'],
+ 'ya' => ['я'],
+ 'yu' => ['ю'],
+ 'zh' => ['ж', 'ჟ', 'ژ'],
+ '(c)' => ['©'],
+ 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
+ 'B' => ['Б', 'Β', 'ब', 'B'],
+ 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
+ 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
+ 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
+ 'F' => ['Ф', 'Φ', 'F'],
+ 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
+ 'H' => ['Η', 'Ή', 'Ħ', 'H'],
+ 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
+ 'J' => ['J'],
+ 'K' => ['К', 'Κ', 'K'],
+ 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
+ 'M' => ['М', 'Μ', 'M'],
+ 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
+ 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
+ 'P' => ['П', 'Π', 'P'],
+ 'Q' => ['Q'],
+ 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
+ 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
+ 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
+ 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
+ 'V' => ['В', 'V'],
+ 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
+ 'X' => ['Χ', 'Ξ', 'X'],
+ 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
+ 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
+ 'AE' => ['Æ', 'Ǽ'],
+ 'Ch' => ['Ч'],
+ 'Dj' => ['Ђ'],
+ 'Dz' => ['Џ'],
+ 'Gx' => ['Ĝ'],
+ 'Hx' => ['Ĥ'],
+ 'Ij' => ['IJ'],
+ 'Jx' => ['Ĵ'],
+ 'Kh' => ['Х'],
+ 'Lj' => ['Љ'],
+ 'Nj' => ['Њ'],
+ 'Oe' => ['Œ'],
+ 'Ps' => ['Ψ'],
+ 'Sh' => ['Ш'],
+ 'Shch' => ['Щ'],
+ 'Ss' => ['ẞ'],
+ 'Th' => ['Þ'],
+ 'Ts' => ['Ц'],
+ 'Ya' => ['Я'],
+ 'Yu' => ['Ю'],
+ 'Zh' => ['Ж'],
+ ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
+ ];
+ }
+
+ /**
+ * Returns the language specific replacements for the ascii method.
+ *
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function languageSpecificCharsArray(string $language): ?array
+ {
+ static $languageSpecific;
+ if (!isset($languageSpecific)) {
+ $languageSpecific = [
+ 'bg' => [
+ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
+ ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
+ ],
+ 'de' => [
+ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
+ ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
+ ],
+ ];
+ }
+
+ return isset($languageSpecific[$language]) ? $languageSpecific[$language] : null;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Traits/Accessable.php b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Accessable.php
new file mode 100644
index 0000000..a477561
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Accessable.php
@@ -0,0 +1,142 @@
+
+ *
+ * @return mixed
+ */
+ public function __get(string $key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * __set.
+ *
+ * @author yansongda
+ *
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ public function __set(string $key, $value)
+ {
+ return $this->set($key, $value);
+ }
+
+ /**
+ * get.
+ *
+ * @author yansongda
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get(?string $key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return method_exists($this, 'toArray') ? $this->toArray() : $default;
+ }
+
+ $method = 'get';
+ foreach (explode('_', $key) as $item) {
+ $method .= ucfirst($item);
+ }
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ return $default;
+ }
+
+ /**
+ * set.
+ *
+ * @author yansongda
+ *
+ * @param mixed $value
+ *
+ * @return $this
+ */
+ public function set(string $key, $value)
+ {
+ $method = 'set';
+ foreach (explode('_', $key) as $item) {
+ $method .= ucfirst($item);
+ }
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}($value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Whether a offset exists.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetexists.php
+ *
+ * @param mixed $offset an offset to check for
+ *
+ * @return bool true on success or false on failure.
+ *
+ * The return value will be casted to boolean if non-boolean was returned.
+ */
+ public function offsetExists($offset)
+ {
+ return !is_null($this->get($offset));
+ }
+
+ /**
+ * Offset to retrieve.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetget.php
+ *
+ * @param mixed $offset the offset to retrieve
+ *
+ * @return mixed can return all value types
+ */
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ /**
+ * Offset to set.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetset.php
+ *
+ * @param mixed $offset the offset to assign the value to
+ * @param mixed $value the value to set
+ *
+ * @return void
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->set($offset, $value);
+ }
+
+ /**
+ * Offset to unset.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetunset.php
+ *
+ * @param mixed $offset the offset to unset
+ *
+ * @return void
+ */
+ public function offsetUnset($offset)
+ {
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Traits/Arrayable.php b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Arrayable.php
new file mode 100644
index 0000000..eb73c6c
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Arrayable.php
@@ -0,0 +1,32 @@
+
+ *
+ * @throws \ReflectionException
+ */
+ public function toArray(): array
+ {
+ $result = [];
+
+ foreach ((new ReflectionClass($this))->getProperties() as $item) {
+ $k = $item->getName();
+ $method = 'get'.Str::studly($k);
+
+ $result[Str::snake($k)] = method_exists($this, $method) ? $this->{$method}() : $this->{$k};
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Traits/HasHttpRequest.php b/server/addons/epay/library/v2/Yansongda/Supports/Traits/HasHttpRequest.php
new file mode 100644
index 0000000..6e98d24
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Traits/HasHttpRequest.php
@@ -0,0 +1,229 @@
+
+ *
+ * @return array|string
+ */
+ public function get(string $endpoint, array $query = [], array $headers = [])
+ {
+ return $this->request('get', $endpoint, [
+ 'headers' => $headers,
+ 'query' => $query,
+ ]);
+ }
+
+ /**
+ * Send a POST request.
+ *
+ * @author yansongda
+ *
+ * @param string|array $data
+ *
+ * @return array|string
+ */
+ public function post(string $endpoint, $data, array $options = [])
+ {
+ if (!is_array($data)) {
+ $options['body'] = $data;
+ } else {
+ $options['form_params'] = $data;
+ }
+
+ return $this->request('post', $endpoint, $options);
+ }
+
+ /**
+ * Send request.
+ *
+ * @author yansongda
+ *
+ * @return array|string
+ */
+ public function request(string $method, string $endpoint, array $options = [])
+ {
+ return $this->unwrapResponse($this->getHttpClient()->{$method}($endpoint, $options));
+ }
+
+ /**
+ * Set http client.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setHttpClient(Client $client): self
+ {
+ $this->httpClient = $client;
+
+ return $this;
+ }
+
+ /**
+ * Return http client.
+ */
+ public function getHttpClient(): Client
+ {
+ if (is_null($this->httpClient)) {
+ $this->httpClient = $this->getDefaultHttpClient();
+ }
+
+ return $this->httpClient;
+ }
+
+ /**
+ * Get default http client.
+ *
+ * @author yansongda
+ */
+ public function getDefaultHttpClient(): Client
+ {
+ return new Client($this->getOptions());
+ }
+
+ /**
+ * setBaseUri.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setBaseUri(string $url): self
+ {
+ if (property_exists($this, 'baseUri')) {
+ $parsedUrl = parse_url($url);
+
+ $this->baseUri = ($parsedUrl['scheme'] ?? 'http').'://'.
+ $parsedUrl['host'].(isset($parsedUrl['port']) ? (':'.$parsedUrl['port']) : '');
+ }
+
+ return $this;
+ }
+
+ /**
+ * getBaseUri.
+ *
+ * @author yansongda
+ */
+ public function getBaseUri(): string
+ {
+ return property_exists($this, 'baseUri') ? $this->baseUri : '';
+ }
+
+ public function getTimeout(): float
+ {
+ return property_exists($this, 'timeout') ? $this->timeout : 5.0;
+ }
+
+ public function setTimeout(float $timeout): self
+ {
+ if (property_exists($this, 'timeout')) {
+ $this->timeout = $timeout;
+ }
+
+ return $this;
+ }
+
+ public function getConnectTimeout(): float
+ {
+ return property_exists($this, 'connectTimeout') ? $this->connectTimeout : 3.0;
+ }
+
+ public function setConnectTimeout(float $connectTimeout): self
+ {
+ if (property_exists($this, 'connectTimeout')) {
+ $this->connectTimeout = $connectTimeout;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get default options.
+ *
+ * @author yansongda
+ */
+ public function getOptions(): array
+ {
+ return array_merge([
+ 'base_uri' => $this->getBaseUri(),
+ 'timeout' => $this->getTimeout(),
+ 'connect_timeout' => $this->getConnectTimeout(),
+ ], $this->getHttpOptions());
+ }
+
+ /**
+ * setOptions.
+ *
+ * @author yansongda
+ *
+ * @return $this
+ */
+ public function setOptions(array $options): self
+ {
+ return $this->setHttpOptions($options);
+ }
+
+ public function getHttpOptions(): array
+ {
+ return $this->httpOptions;
+ }
+
+ public function setHttpOptions(array $httpOptions): self
+ {
+ $this->httpOptions = $httpOptions;
+
+ return $this;
+ }
+
+ /**
+ * Convert response.
+ *
+ * @author yansongda
+ *
+ * @return array|string
+ */
+ public function unwrapResponse(ResponseInterface $response)
+ {
+ $contentType = $response->getHeaderLine('Content-Type');
+ $contents = $response->getBody()->getContents();
+
+ if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) {
+ return json_decode($contents, true);
+ } elseif (false !== stripos($contentType, 'xml')) {
+ return json_decode(json_encode(simplexml_load_string($contents, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
+ }
+
+ return $contents;
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Traits/Serializable.php b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Serializable.php
new file mode 100644
index 0000000..5e2010b
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Traits/Serializable.php
@@ -0,0 +1,85 @@
+
+ *
+ * @return string
+ */
+ public function toJson()
+ {
+ return $this->serialize();
+ }
+
+ /**
+ * Specify data which should be serialized to JSON.
+ *
+ * @see https://php.net/manual/en/jsonserializable.jsonserialize.php
+ *
+ * @return mixed data which can be serialized by json_encode ,
+ * which is a value of any type other than a resource
+ *
+ * @since 5.4.0
+ */
+ public function jsonSerialize()
+ {
+ if (method_exists($this, 'toArray')) {
+ return $this->toArray();
+ }
+
+ return [];
+ }
+
+ /**
+ * String representation of object.
+ *
+ * @see https://php.net/manual/en/serializable.serialize.php
+ *
+ * @return string the string representation of the object or null
+ *
+ * @since 5.1.0
+ */
+ public function serialize()
+ {
+ if (method_exists($this, 'toArray')) {
+ return json_encode($this->toArray());
+ }
+
+ return json_encode([]);
+ }
+
+ /**
+ * Constructs the object.
+ *
+ * @see https://php.net/manual/en/serializable.unserialize.php
+ *
+ * @param string $serialized
+ * The string representation of the object.
+ *
+ *
+ * @since 5.1.0
+ */
+ public function unserialize($serialized)
+ {
+ $data = json_decode($serialized, true);
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new RuntimeException('Invalid Json Format');
+ }
+
+ foreach ($data as $key => $item) {
+ if (method_exists($this, 'set')) {
+ $this->set($key, $item);
+ }
+ }
+ }
+}
diff --git a/server/addons/epay/library/v2/Yansongda/Supports/Traits/ShouldThrottle.php b/server/addons/epay/library/v2/Yansongda/Supports/Traits/ShouldThrottle.php
new file mode 100644
index 0000000..cbdeb71
--- /dev/null
+++ b/server/addons/epay/library/v2/Yansongda/Supports/Traits/ShouldThrottle.php
@@ -0,0 +1,147 @@
+ 60,
+ 'period' => 60,
+ 'count' => 0,
+ 'reset_time' => 0,
+ ];
+
+ /**
+ * isThrottled.
+ *
+ * @author yansongda
+ *
+ * @param string $key
+ * @param int $limit
+ * @param int $period
+ * @param bool $auto_add
+ *
+ * @return bool
+ */
+ public function isThrottled($key, $limit = 60, $period = 60, $auto_add = false)
+ {
+ if (-1 === $limit) {
+ return false;
+ }
+
+ $now = microtime(true) * 1000;
+
+ $this->redis->zremrangebyscore($key, 0, $now - $period * 1000);
+
+ $this->_throttle = [
+ 'limit' => $limit,
+ 'period' => $period,
+ 'count' => $this->getThrottleCounts($key, $period),
+ 'reset_time' => $this->getThrottleResetTime($key, $now),
+ ];
+
+ if ($this->_throttle['count'] < $limit) {
+ if ($auto_add) {
+ $this->throttleAdd($key, $period);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 限流 + 1.
+ *
+ * @author yansongda
+ *
+ * @param string $key
+ * @param int $period
+ */
+ public function throttleAdd($key, $period = 60)
+ {
+ $now = microtime(true) * 1000;
+
+ $this->redis->zadd($key, [$now => $now]);
+ $this->redis->expire($key, $period * 2);
+ }
+
+ /**
+ * getResetTime.
+ *
+ * @author yansongda
+ *
+ * @param $key
+ * @param $now
+ *
+ * @return int
+ */
+ public function getThrottleResetTime($key, $now)
+ {
+ $data = $this->redis->zrangebyscore(
+ $key,
+ $now - $this->_throttle['period'] * 1000,
+ $now,
+ ['limit' => [0, 1]]
+ );
+
+ if (0 === count($data)) {
+ return $this->_throttle['reset_time'] = time() + $this->_throttle['period'];
+ }
+
+ return intval($data[0] / 1000) + $this->_throttle['period'];
+ }
+
+ /**
+ * 获取限流相关信息.
+ *
+ * @author yansongda
+ *
+ * @param string|null $key
+ * @param mixed|null $default
+ *
+ * @return array|null
+ */
+ public function getThrottleInfo($key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return $this->_throttle;
+ }
+
+ if (isset($this->_throttle[$key])) {
+ return $this->_throttle[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * 获取已使用次数.
+ *
+ * @author yansongda
+ *
+ * @param string $key
+ * @param int $period
+ *
+ * @return string
+ */
+ public function getThrottleCounts($key, $period = 60)
+ {
+ $now = microtime(true) * 1000;
+
+ return $this->redis->zcount($key, $now - $period * 1000, $now);
+ }
+}
diff --git a/server/addons/epay/library/v2/version.txt b/server/addons/epay/library/v2/version.txt
new file mode 100644
index 0000000..d92b16e
--- /dev/null
+++ b/server/addons/epay/library/v2/version.txt
@@ -0,0 +1 @@
+v2.10.6
\ No newline at end of file
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Contract/ConfigInterface.php b/server/addons/epay/library/v3/Yansongda/Pay/Contract/ConfigInterface.php
new file mode 100644
index 0000000..5b0cf67
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Contract/ConfigInterface.php
@@ -0,0 +1,22 @@
+getBody();
+
+ if (!is_null($result = $packer->unpack($body))) {
+ return $result;
+ }
+
+ throw new InvalidResponseException(Exception::UNPACK_RESPONSE_ERROR, 'Unpack Response Error', ['body' => $body, 'response' => $response]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Direction/CollectionDirection.php b/server/addons/epay/library/v3/Yansongda/Pay/Direction/CollectionDirection.php
new file mode 100644
index 0000000..21562db
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Direction/CollectionDirection.php
@@ -0,0 +1,27 @@
+parse($packer, $response)
+ );
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Direction/NoHttpRequestDirection.php b/server/addons/epay/library/v3/Yansongda/Pay/Direction/NoHttpRequestDirection.php
new file mode 100644
index 0000000..fb9f631
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Direction/NoHttpRequestDirection.php
@@ -0,0 +1,17 @@
+{$method}(...$args);
+
+ return;
+ }
+
+ throw new InvalidConfigException(Exception\Exception::EVENT_CONFIG_ERROR);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Event/ApiRequested.php b/server/addons/epay/library/v3/Yansongda/Pay/Event/ApiRequested.php
new file mode 100644
index 0000000..c5539ca
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Event/ApiRequested.php
@@ -0,0 +1,9 @@
+provider = $provider;
+ $this->contents = $contents;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Event/Event.php b/server/addons/epay/library/v3/Yansongda/Pay/Event/Event.php
new file mode 100644
index 0000000..7947c1e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Event/Event.php
@@ -0,0 +1,17 @@
+rocket = $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Event/MethodCalled.php b/server/addons/epay/library/v3/Yansongda/Pay/Event/MethodCalled.php
new file mode 100644
index 0000000..f83b73f
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Event/MethodCalled.php
@@ -0,0 +1,25 @@
+provider = $provider;
+ $this->name = $name;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Event/PayFinish.php b/server/addons/epay/library/v3/Yansongda/Pay/Event/PayFinish.php
new file mode 100644
index 0000000..b913ca4
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Event/PayFinish.php
@@ -0,0 +1,9 @@
+plugins = $plugins;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Exception/ContainerException.php b/server/addons/epay/library/v3/Yansongda/Pay/Exception/ContainerException.php
new file mode 100644
index 0000000..1753417
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Exception/ContainerException.php
@@ -0,0 +1,19 @@
+extra = $extra;
+
+ parent::__construct($message, $code, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Exception/InvalidConfigException.php b/server/addons/epay/library/v3/Yansongda/Pay/Exception/InvalidConfigException.php
new file mode 100644
index 0000000..903a10c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Exception/InvalidConfigException.php
@@ -0,0 +1,18 @@
+response = $extra;
+ $this->exception = $exception;
+
+ parent::__construct($message, $code, $extra, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Exception/ServiceException.php b/server/addons/epay/library/v3/Yansongda/Pay/Exception/ServiceException.php
new file mode 100644
index 0000000..0a848de
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Exception/ServiceException.php
@@ -0,0 +1,18 @@
+get('alipay');
+
+ return $alipay[get_tenant($params)] ?? [];
+ }
+}
+
+if (!function_exists('get_public_cert')) {
+ function get_public_cert(string $key): string
+ {
+ return Str::endsWith($key, ['.cer', '.crt', '.pem']) ? file_get_contents($key) : $key;
+ }
+}
+
+if (!function_exists('get_private_cert')) {
+ function get_private_cert(string $key): string
+ {
+ if (Str::endsWith($key, ['.crt', '.pem'])) {
+ return file_get_contents($key);
+ }
+
+ return "-----BEGIN RSA PRIVATE KEY-----\n".
+ wordwrap($key, 64, "\n", true).
+ "\n-----END RSA PRIVATE KEY-----";
+ }
+}
+
+if (!function_exists('verify_alipay_sign')) {
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ * @throws InvalidResponseException
+ */
+ function verify_alipay_sign(array $params, string $contents, string $sign): void
+ {
+ $public = get_alipay_config($params)['alipay_public_cert_path'] ?? null;
+
+ if (empty($public)) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [alipay_public_cert_path]');
+ }
+
+ // 优化publickey判断
+ if (!Str::endsWith($public, ['.cer', '.crt', '.pem']) && stripos($public, '-----BEGIN PUBLIC KEY-----') === false) {
+ $public = "-----BEGIN PUBLIC KEY-----\n".
+ wordwrap($public, 64, "\n", true).
+ "\n-----END PUBLIC KEY-----";
+ }
+
+ $result = 1 === openssl_verify(
+ $contents,
+ base64_decode($sign),
+ get_public_cert($public),
+ OPENSSL_ALGO_SHA256
+ );
+
+ if (!$result) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, 'Verify Alipay Response Sign Failed', func_get_args());
+ }
+ }
+}
+
+if (!function_exists('get_wechat_config')) {
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ function get_wechat_config(array $params): array
+ {
+ $wechat = Pay::get(ConfigInterface::class)->get('wechat');
+
+ return $wechat[get_tenant($params)] ?? [];
+ }
+}
+
+if (!function_exists('get_wechat_base_uri')) {
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ function get_wechat_base_uri(array $params): string
+ {
+ $config = get_wechat_config($params);
+
+ return Wechat::URL[$config['mode'] ?? Pay::MODE_NORMAL];
+ }
+}
+
+if (!function_exists('get_wechat_sign')) {
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ */
+ function get_wechat_sign(array $params, string $contents): string
+ {
+ $privateKey = get_wechat_config($params)['mch_secret_cert'] ?? null;
+
+ if (empty($privateKey)) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_secret_cert]');
+ }
+
+ $privateKey = get_private_cert($privateKey);
+
+ openssl_sign($contents, $sign, $privateKey, 'sha256WithRSAEncryption');
+
+ return base64_encode($sign);
+ }
+}
+
+if (!function_exists('get_wechat_sign_v2')) {
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ */
+ function get_wechat_sign_v2(array $params, array $payload, bool $upper = true): string
+ {
+ $key = get_wechat_config($params)['mch_secret_key_v2'] ?? null;
+
+ if (empty($key)) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_secret_key_v2]');
+ }
+
+ ksort($payload);
+
+ $buff = '';
+
+ foreach ($payload as $k => $v) {
+ $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+ }
+
+ $sign = md5($buff.'key='.$key);
+
+ return $upper ? strtoupper($sign) : $sign;
+ }
+}
+
+if (!function_exists('verify_wechat_sign')) {
+ /**
+ * @param ResponseInterface|ServerRequestInterface $message
+ *
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidResponseException
+ * @throws ServiceNotFoundException
+ * @throws InvalidParamsException
+ */
+ function verify_wechat_sign(MessageInterface $message, array $params): void
+ {
+ if ($message instanceof ServerRequestInterface && 'localhost' === $message->getUri()->getHost()) {
+ return;
+ }
+
+ $wechatSerial = $message->getHeaderLine('Wechatpay-Serial');
+ $timestamp = $message->getHeaderLine('Wechatpay-Timestamp');
+ $random = $message->getHeaderLine('Wechatpay-Nonce');
+ $sign = $message->getHeaderLine('Wechatpay-Signature');
+ $body = (string) $message->getBody();
+
+ $content = $timestamp."\n".$random."\n".$body."\n";
+ $public = get_wechat_config($params)['wechat_public_cert_path'][$wechatSerial] ?? null;
+
+ if (empty($sign)) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', ['headers' => $message->getHeaders(), 'body' => $body]);
+ }
+
+ $public = get_public_cert(
+ empty($public) ? reload_wechat_public_certs($params, $wechatSerial) : $public
+ );
+
+ $result = 1 === openssl_verify(
+ $content,
+ base64_decode($sign),
+ $public,
+ 'sha256WithRSAEncryption'
+ );
+
+ if (!$result) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', ['headers' => $message->getHeaders(), 'body' => $body]);
+ }
+ }
+}
+
+if (!function_exists('encrypt_wechat_contents')) {
+ function encrypt_wechat_contents(string $contents, string $publicKey): ?string
+ {
+ if (openssl_public_encrypt($contents, $encrypted, get_public_cert($publicKey), OPENSSL_PKCS1_OAEP_PADDING)) {
+ return base64_encode($encrypted);
+ }
+
+ return null;
+ }
+}
+
+if (!function_exists('reload_wechat_public_certs')) {
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ * @throws InvalidParamsException
+ * @throws InvalidResponseException
+ */
+ function reload_wechat_public_certs(array $params, ?string $serialNo = null): string
+ {
+ $data = Pay::wechat()->pay(
+ [PreparePlugin::class, WechatPublicCertsPlugin::class, RadarSignPlugin::class, ParserPlugin::class],
+ $params
+ )->get('data', []);
+
+ foreach ($data as $item) {
+ $certs[$item['serial_no']] = decrypt_wechat_resource($item['encrypt_certificate'], $params)['ciphertext'] ?? '';
+ }
+
+ $wechatConfig = get_wechat_config($params);
+
+ Pay::get(ConfigInterface::class)->set(
+ 'wechat.'.get_tenant($params).'.wechat_public_cert_path',
+ ((array) ($wechatConfig['wechat_public_cert_path'] ?? [])) + ($certs ?? [])
+ );
+
+ if (!is_null($serialNo) && empty($certs[$serialNo])) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Get Wechat Public Cert Error');
+ }
+
+ return $certs[$serialNo] ?? '';
+ }
+}
+
+if (!function_exists('get_wechat_public_certs')) {
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ * @throws InvalidParamsException
+ * @throws InvalidResponseException
+ */
+ function get_wechat_public_certs(array $params = [], ?string $path = null): void
+ {
+ reload_wechat_public_certs($params);
+
+ $config = get_wechat_config($params);
+
+ if (empty($path)) {
+ var_dump($config['wechat_public_cert_path']);
+
+ return;
+ }
+
+ foreach ($config['wechat_public_cert_path'] as $serialNo => $cert) {
+ file_put_contents($path.'/'.$serialNo.'.crt', $cert);
+ }
+ }
+}
+
+if (!function_exists('decrypt_wechat_resource')) {
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidResponseException
+ * @throws ServiceNotFoundException
+ */
+ function decrypt_wechat_resource(array $resource, array $params): array
+ {
+ $ciphertext = base64_decode($resource['ciphertext'] ?? '');
+ $secret = get_wechat_config($params)['mch_secret_key'] ?? null;
+
+ if (strlen($ciphertext) <= Wechat::AUTH_TAG_LENGTH_BYTE) {
+ throw new InvalidResponseException(Exception::INVALID_CIPHERTEXT_PARAMS);
+ }
+
+ if (is_null($secret) || Wechat::MCH_SECRET_KEY_LENGTH_BYTE != strlen($secret)) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_secret_key]');
+ }
+
+ switch ($resource['algorithm'] ?? '') {
+ case 'AEAD_AES_256_GCM':
+ $resource['ciphertext'] = decrypt_wechat_resource_aes_256_gcm($ciphertext, $secret, $resource['nonce'] ?? '', $resource['associated_data'] ?? '');
+
+ break;
+
+ default:
+ throw new InvalidResponseException(Exception::INVALID_REQUEST_ENCRYPTED_METHOD);
+ }
+
+ return $resource;
+ }
+}
+
+if (!function_exists('decrypt_wechat_resource_aes_256_gcm')) {
+ /**
+ * @return array|string
+ *
+ * @throws InvalidResponseException
+ */
+ function decrypt_wechat_resource_aes_256_gcm(string $ciphertext, string $secret, string $nonce, string $associatedData)
+ {
+ $decrypted = openssl_decrypt(
+ substr($ciphertext, 0, -Wechat::AUTH_TAG_LENGTH_BYTE),
+ 'aes-256-gcm',
+ $secret,
+ OPENSSL_RAW_DATA,
+ $nonce,
+ substr($ciphertext, -Wechat::AUTH_TAG_LENGTH_BYTE),
+ $associatedData
+ );
+
+ if ('certificate' !== $associatedData) {
+ $decrypted = json_decode(strval($decrypted), true);
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new InvalidResponseException(Exception::INVALID_REQUEST_ENCRYPTED_DATA);
+ }
+ }
+
+ return $decrypted;
+ }
+}
+
+if (!function_exists('get_unipay_config')) {
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ function get_unipay_config(array $params): array
+ {
+ $unipay = Pay::get(ConfigInterface::class)->get('unipay');
+
+ return $unipay[get_tenant($params)] ?? [];
+ }
+}
+
+if (!function_exists('verify_unipay_sign')) {
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidResponseException
+ * @throws ServiceNotFoundException
+ */
+ function verify_unipay_sign(array $params, string $contents, string $sign): void
+ {
+ if (empty($params['signPubKeyCert'])
+ && empty($public = get_unipay_config($params)['unipay_public_cert_path'] ?? null)) {
+ throw new InvalidConfigException(Exception::UNIPAY_CONFIG_ERROR, 'Missing Unipay Config -- [unipay_public_cert_path]');
+ }
+
+ $result = 1 === openssl_verify(
+ hash('sha256', $contents),
+ base64_decode($sign),
+ get_public_cert($params['signPubKeyCert'] ?? $public ?? ''),
+ 'sha256'
+ );
+
+ if (!$result) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, 'Verify Unipay Response Sign Failed', func_get_args());
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Logger.php b/server/addons/epay/library/v3/Yansongda/Pay/Logger.php
new file mode 100644
index 0000000..4643160
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Logger.php
@@ -0,0 +1,48 @@
+get('logger.enable', false)) {
+ return;
+ }
+
+ $class = Pay::get(LoggerInterface::class);
+
+ if ($class instanceof \Psr\Log\LoggerInterface || $class instanceof \Yansongda\Supports\Logger) {
+ $class->{$method}(...$args);
+
+ return;
+ }
+
+ throw new InvalidConfigException(Exception\Exception::LOGGER_CONFIG_ERROR);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Packer/JsonPacker.php b/server/addons/epay/library/v3/Yansongda/Pay/Packer/JsonPacker.php
new file mode 100644
index 0000000..3c64202
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Packer/JsonPacker.php
@@ -0,0 +1,26 @@
+';
+
+ foreach ($payload as $key => $val) {
+ $xml .= is_numeric($val) ? '<'.$key.'>'.$val.''.$key.'>' :
+ '<'.$key.'>'.$key.'>';
+ }
+
+ $xml .= '';
+
+ return $xml;
+ }
+
+ public function unpack(string $payload): ?array
+ {
+ if (empty($payload)) {
+ return [];
+ }
+
+ if (PHP_VERSION_ID < 80000) {
+ libxml_disable_entity_loader();
+ }
+
+ $data = json_decode(json_encode(
+ simplexml_load_string($payload, 'SimpleXMLElement', LIBXML_NOCDATA),
+ JSON_UNESCAPED_UNICODE
+ ), true);
+
+ if (JSON_ERROR_NONE === json_last_error()) {
+ return $data;
+ }
+
+ return null;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Pay.php b/server/addons/epay/library/v3/Yansongda/Pay/Pay.php
new file mode 100644
index 0000000..7f3c799
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Pay.php
@@ -0,0 +1,267 @@
+registerServices($config, $container);
+
+ Pay::set(DirectionInterface::class, CollectionDirection::class);
+ Pay::set(PackerInterface::class, JsonPacker::class);
+ }
+
+ /**
+ * @return mixed
+ *
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ public static function __callStatic(string $service, array $config)
+ {
+ if (!empty($config)) {
+ self::config(...$config);
+ }
+
+ return self::get($service);
+ }
+
+ /**
+ * @param null|Closure|ContainerInterface $container
+ *
+ * @throws ContainerException
+ */
+ public static function config(array $config = [], $container = null): bool
+ {
+ if (self::hasContainer() && !($config['_force'] ?? false)) {
+ return false;
+ }
+
+ new self($config, $container);
+
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ *
+ * @param mixed $value
+ *
+ * @throws ContainerException
+ */
+ public static function set(string $name, $value): void
+ {
+ try {
+ $container = Pay::getContainer();
+
+ if ($container instanceof LaravelContainer) {
+ $container->singleton($name, $value instanceof Closure ? $value : static fn () => $value);
+
+ return;
+ }
+
+ if (method_exists($container, 'set')) {
+ $container->set(...func_get_args());
+
+ return;
+ }
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException($e->getMessage());
+ }
+
+ throw new ContainerException('Current container does NOT support `set` method');
+ }
+
+ /**
+ * @codeCoverageIgnore
+ *
+ * @return mixed
+ *
+ * @throws ContainerException
+ */
+ public static function make(string $service, array $parameters = [])
+ {
+ try {
+ $container = Pay::getContainer();
+
+ if (method_exists($container, 'make')) {
+ return $container->make(...func_get_args());
+ }
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException($e->getMessage());
+ }
+
+ $parameters = array_values($parameters);
+
+ return new $service(...$parameters);
+ }
+
+ /**
+ * @return mixed
+ *
+ * @throws ServiceNotFoundException
+ * @throws ContainerException
+ */
+ public static function get(string $service)
+ {
+ try {
+ return Pay::getContainer()->get($service);
+ } catch (NotFoundExceptionInterface $e) {
+ throw new ServiceNotFoundException($e->getMessage());
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException($e->getMessage());
+ }
+ }
+
+ /**
+ * @throws ContainerNotFoundException
+ */
+ public static function has(string $service): bool
+ {
+ return Pay::getContainer()->has($service);
+ }
+
+ /**
+ * @param null|Closure|ContainerInterface $container
+ */
+ public static function setContainer($container): void
+ {
+ self::$container = $container;
+ }
+
+ /**
+ * @throws ContainerNotFoundException
+ */
+ public static function getContainer(): ContainerInterface
+ {
+ if (self::$container instanceof ContainerInterface) {
+ return self::$container;
+ }
+
+ if (self::$container instanceof Closure) {
+ return (self::$container)();
+ }
+
+ throw new ContainerNotFoundException('`getContainer()` failed! Maybe you should `setContainer()` first', Exception\Exception::CONTAINER_NOT_FOUND);
+ }
+
+ public static function hasContainer(): bool
+ {
+ return self::$container instanceof ContainerInterface || self::$container instanceof Closure;
+ }
+
+ public static function clear(): void
+ {
+ self::$container = null;
+ }
+
+ /**
+ * @param mixed $data
+ *
+ * @throws ContainerException
+ */
+ public static function registerService(string $service, $data): void
+ {
+ $var = new $service();
+
+ if ($var instanceof ServiceProviderInterface) {
+ $var->register($data);
+ }
+ }
+
+ /**
+ * @param null|Closure|ContainerInterface $container
+ *
+ * @throws ContainerException
+ */
+ private function registerServices(array $config, $container = null): void
+ {
+ foreach (array_merge($this->coreService, $this->service) as $service) {
+ self::registerService($service, ContainerServiceProvider::class == $service ? $container : $config);
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/CallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/CallbackPlugin.php
new file mode 100644
index 0000000..48ea386
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/CallbackPlugin.php
@@ -0,0 +1,65 @@
+ $rocket]);
+
+ $this->formatPayload($rocket);
+ $sign = $rocket->getParams()['sign'] ?? false;
+
+ if (!$sign) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', $rocket->getParams());
+ }
+
+ verify_alipay_sign($rocket->getParams(), $this->getSignContent($rocket->getPayload()), $sign);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload())
+ ;
+
+ Logger::info('[alipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function formatPayload(Rocket $rocket): void
+ {
+ $payload = (new Collection($rocket->getParams()))
+ ->filter(fn ($v, $k) => '' !== $v && !is_null($v) && 'sign' != $k && 'sign_type' != $k && !Str::startsWith($k, '_'))
+ ;
+
+ $rocket->setPayload($payload);
+ }
+
+ protected function getSignContent(Collection $payload): string
+ {
+ return $payload->sortKeys()->toString();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillDownloadUrlQueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillDownloadUrlQueryPlugin.php
new file mode 100644
index 0000000..f885b10
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillDownloadUrlQueryPlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.bill.ereceipt.apply',
+ 'biz_content' => array_merge(
+ [
+ 'type' => 'FUND_DETAIL',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][BillEreceiptApplyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillEreceiptQueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillEreceiptQueryPlugin.php
new file mode 100644
index 0000000..2e74009
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Data/BillEreceiptQueryPlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.ebpp.pdeduct.pay',
+ 'biz_content' => array_merge(
+ [
+ 'agent_channel' => 'PUBLICFORM',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][PdeductPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignAddPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignAddPlugin.php
new file mode 100644
index 0000000..5033698
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignAddPlugin.php
@@ -0,0 +1,37 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.ebpp.pdeduct.sign.add',
+ 'biz_content' => array_merge(
+ [
+ 'charge_inst' => 'CQCENTERELECTRIC',
+ 'agent_channel' => 'PUBLICPLATFORM',
+ 'deduct_prod_code' => 'INST_DIRECT_DEDUCT',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][PdeductSignAddPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignCancelPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignCancelPlugin.php
new file mode 100644
index 0000000..83af535
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Ebpp/PdeductSignCancelPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.ebpp.pdeduct.sign.cancel',
+ 'biz_content' => array_merge(
+ [
+ 'agent_channel' => 'PUBLICPLATFORM',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][PdeductSignCancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AccountQueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AccountQueryPlugin.php
new file mode 100644
index 0000000..1e3d54e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AccountQueryPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.account.query',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][AccountQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderFreezePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderFreezePlugin.php
new file mode 100644
index 0000000..72edabb
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderFreezePlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.order.freeze',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'PRE_AUTH',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[alipay][AuthOrderFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderUnfreezePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderUnfreezePlugin.php
new file mode 100644
index 0000000..d7bbfa3
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/AuthOrderUnfreezePlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.trans.common.query',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
+ 'biz_scene' => 'DIRECT_TRANSFER',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][TransCommonQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransPagePayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransPagePayPlugin.php
new file mode 100644
index 0000000..a5511f2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransPagePayPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.fund.trans.page.pay',
+ 'biz_content' => $rocket->getParams(),
+ ])
+ ;
+
+ Logger::info('[alipay][TransPagePayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransTobankTransferPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransTobankTransferPlugin.php
new file mode 100644
index 0000000..394584c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Fund/TransTobankTransferPlugin.php
@@ -0,0 +1,15 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.trans.uni.transfer',
+ 'biz_content' => array_merge(
+ [
+ 'biz_scene' => 'DIRECT_TRANSFER',
+ 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][TransUniTransferPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/GeneralPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/GeneralPlugin.php
new file mode 100644
index 0000000..36a7313
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/GeneralPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $this->doSomethingBefore($rocket);
+
+ $rocket->mergePayload([
+ 'method' => $this->getMethod(),
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[alipay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function doSomethingBefore(Rocket $rocket): void
+ {
+ }
+
+ abstract protected function getMethod(): string;
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/HtmlResponsePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/HtmlResponsePlugin.php
new file mode 100644
index 0000000..40b018c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/HtmlResponsePlugin.php
@@ -0,0 +1,71 @@
+ $rocket]);
+
+ $radar = $rocket->getRadar();
+
+ $response = 'GET' === $radar->getMethod() ?
+ $this->buildRedirect($radar->getUri()->__toString(), $rocket->getPayload()) :
+ $this->buildHtml($radar->getUri()->__toString(), $rocket->getPayload());
+
+ $rocket->setDestination($response);
+
+ Logger::info('[alipay][HtmlResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function buildRedirect(string $endpoint, Collection $payload): Response
+ {
+ $url = $endpoint.(false === strpos($endpoint, '?') ? '?' : '&').$payload->query();
+
+ $content = sprintf(
+ '
+
+
+
+
+
+ Redirecting to %1$s
+
+
+ Redirecting to %1$s.
+
+ ',
+ htmlspecialchars($url, ENT_QUOTES)
+ );
+
+ return new Response(302, ['Location' => $url], $content);
+ }
+
+ protected function buildHtml(string $endpoint, Collection $payload): Response
+ {
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response(200, [], $sHtml);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/LaunchPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/LaunchPlugin.php
new file mode 100644
index 0000000..025225e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/LaunchPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ if (should_do_http_request($rocket->getDirection())) {
+ $response = Collection::wrap($rocket->getDestination());
+ $result = $response->get($this->getResultKey($rocket->getPayload()));
+
+ $this->verifySign($rocket->getParams(), $response, $result);
+
+ $rocket->setDestination(Collection::wrap($result));
+ }
+
+ Logger::info('[alipay][LaunchPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidResponseException
+ * @throws ServiceNotFoundException
+ */
+ protected function verifySign(array $params, Collection $response, ?array $result): void
+ {
+ $sign = $response->get('sign', '');
+
+ if ('' === $sign || is_null($result)) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, 'Verify Alipay Response Sign Failed: sign is empty', $response);
+ }
+
+ verify_alipay_sign($params, json_encode($result, JSON_UNESCAPED_UNICODE), $sign);
+ }
+
+ protected function getResultKey(Collection $payload): string
+ {
+ $method = $payload->get('method');
+
+ return str_replace('.', '_', $method).'_response';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/PreparePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/PreparePlugin.php
new file mode 100644
index 0000000..b4ace01
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/PreparePlugin.php
@@ -0,0 +1,214 @@
+ $rocket]);
+
+ $rocket->mergePayload($this->getPayload($rocket->getParams()));
+
+ Logger::info('[alipay][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ */
+ protected function getPayload(array $params): array
+ {
+ $tenant = get_tenant($params);
+ $config = get_alipay_config($params);
+
+ $payload = [
+ 'app_id' => $config['app_id'] ?? '',
+ 'method' => '',
+ 'format' => 'JSON',
+ 'return_url' => $this->getReturnUrl($params, $config),
+ 'charset' => 'utf-8',
+ 'sign_type' => 'RSA2',
+ 'sign' => '',
+ 'timestamp' => date('Y-m-d H:i:s'),
+ 'version' => '1.0',
+ 'notify_url' => $this->getNotifyUrl($params, $config),
+ 'app_auth_token' => $this->getAppAuthToken($params, $config),
+ 'biz_content' => [],
+ ];
+ if (!empty($config['app_cert_public_key']) && !empty($config['alipay_root_cert'])) {
+ $payload = array_merge($payload, ['app_cert_sn' => $this->getAppCertSn($tenant, $config), 'alipay_root_cert_sn' => $this->getAlipayRootCertSn($tenant, $config)]);
+ }
+ return $payload;
+ }
+
+ protected function getReturnUrl(array $params, array $config): string
+ {
+ if (!empty($params['_return_url'])) {
+ return $params['_return_url'];
+ }
+
+ return $config['return_url'] ?? '';
+ }
+
+ protected function getNotifyUrl(array $params, array $config): string
+ {
+ if (!empty($params['_notify_url'])) {
+ return $params['_notify_url'];
+ }
+
+ return $config['notify_url'] ?? '';
+ }
+
+ protected function getAppAuthToken(array $params, array $config): string
+ {
+ if (!empty($params['_app_auth_token'])) {
+ return $params['_app_auth_token'];
+ }
+
+ return $config['app_auth_token'] ?? '';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getAppCertSn(string $tenant, array $config): string
+ {
+ if (!empty($config['app_public_cert_sn'])) {
+ return $config['app_public_cert_sn'];
+ }
+
+ $path = $config['app_public_cert_path'] ?? null;
+
+ if (is_null($path)) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_public_cert_path]');
+ }
+
+ $cert = file_get_contents($path);
+ $ssl = openssl_x509_parse($cert);
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Parse `app_public_cert_path` Error');
+ }
+
+ $result = $this->getCertSn($ssl['issuer'] ?? [], $ssl['serialNumber'] ?? '');
+
+ Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.app_public_cert_sn', $result);
+
+ return $result;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getAlipayRootCertSn(string $tenant, array $config): string
+ {
+ if (!empty($config['alipay_root_cert_sn'])) {
+ return $config['alipay_root_cert_sn'];
+ }
+
+ $path = $config['alipay_root_cert_path'] ?? null;
+
+ if (is_null($path)) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [alipay_root_cert_path]');
+ }
+
+ $sn = '';
+ $exploded = explode('-----END CERTIFICATE-----', file_get_contents($path));
+
+ foreach ($exploded as $cert) {
+ if (empty(trim($cert))) {
+ continue;
+ }
+
+ $ssl = openssl_x509_parse($cert.'-----END CERTIFICATE-----');
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Invalid alipay_root_cert');
+ }
+
+ $detail = $this->formatCert($ssl);
+
+ if ('sha1WithRSAEncryption' == $detail['signatureTypeLN'] || 'sha256WithRSAEncryption' == $detail['signatureTypeLN']) {
+ $sn .= $this->getCertSn($detail['issuer'], $detail['serialNumber']).'_';
+ }
+ }
+
+ $result = substr($sn, 0, -1);
+
+ Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.alipay_root_cert_sn', $result);
+
+ return $result;
+ }
+
+ protected function getCertSn(array $issuer, string $serialNumber): string
+ {
+ return md5(
+ $this->array2string(array_reverse($issuer)).$serialNumber
+ );
+ }
+
+ protected function array2string(array $array): string
+ {
+ $string = [];
+
+ foreach ($array as $key => $value) {
+ $string[] = $key.'='.$value;
+ }
+
+ return implode(',', $string);
+ }
+
+ protected function formatCert(array $ssl): array
+ {
+ if (0 === strpos($ssl['serialNumber'] ?? '', '0x')) {
+ $ssl['serialNumber'] = $this->hex2dec($ssl['serialNumberHex'] ?? '');
+ }
+
+ return $ssl;
+ }
+
+ protected function hex2dec(string $hex): string
+ {
+ $dec = '0';
+ $len = strlen($hex);
+
+ for ($i = 1; $i <= $len; ++$i) {
+ $dec = bcadd(
+ $dec,
+ bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i), 0), 0),
+ 0
+ );
+ }
+
+ return $dec;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/RadarSignPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/RadarSignPlugin.php
new file mode 100644
index 0000000..b8f3d57
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/RadarSignPlugin.php
@@ -0,0 +1,144 @@
+ $rocket]);
+
+ $this->sign($rocket);
+
+ $this->reRadar($rocket);
+
+ Logger::info('[alipay][RadarSignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function sign(Rocket $rocket): void
+ {
+ $this->formatPayload($rocket);
+
+ $sign = $this->getSign($rocket);
+
+ $rocket->mergePayload(['sign' => $sign]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function reRadar(Rocket $rocket): void
+ {
+ $params = $rocket->getParams();
+
+ $rocket->setRadar(new Request(
+ $this->getMethod($params),
+ $this->getUrl($params),
+ $this->getHeaders(),
+ $this->getBody($rocket->getPayload()),
+ ));
+ }
+
+ protected function formatPayload(Rocket $rocket): void
+ {
+ $payload = $rocket->getPayload()->filter(fn ($v, $k) => '' !== $v && !is_null($v) && 'sign' != $k);
+
+ $contents = array_filter($payload->get('biz_content', []), fn ($v, $k) => !Str::startsWith(strval($k), '_'), ARRAY_FILTER_USE_BOTH);
+
+ $rocket->setPayload(
+ $payload->merge(['biz_content' => json_encode($contents)])
+ );
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getSign(Rocket $rocket): string
+ {
+ $privateKey = $this->getPrivateKey($rocket->getParams());
+
+ $content = $rocket->getPayload()->sortKeys()->toString();
+
+ openssl_sign($content, $sign, $privateKey, OPENSSL_ALGO_SHA256);
+
+ return base64_encode($sign);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getPrivateKey(array $params): string
+ {
+ $privateKey = get_alipay_config($params)['app_secret_cert'] ?? null;
+
+ if (is_null($privateKey)) {
+ throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_secret_cert]');
+ }
+
+ return get_private_cert($privateKey);
+ }
+
+ protected function getMethod(array $params): string
+ {
+ return strtoupper($params['_method'] ?? 'POST');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUrl(array $params): string
+ {
+ $config = get_alipay_config($params);
+
+ return Alipay::URL[$config['mode'] ?? Pay::MODE_NORMAL];
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'Content-Type' => 'application/x-www-form-urlencoded',
+ ];
+ }
+
+ protected function getBody(Collection $payload): string
+ {
+ return $payload->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/AppShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/AppShortcut.php
new file mode 100644
index 0000000..d40509c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/AppShortcut.php
@@ -0,0 +1,47 @@
+buildResponse(),
+ ];
+ }
+
+ protected function buildResponse(): PluginInterface
+ {
+ return new class() implements PluginInterface {
+ public function assembly(Rocket $rocket, Closure $next): Rocket
+ {
+ $rocket->setDestination(new Response());
+
+ /* @var Rocket $rocket */
+ $rocket = $next($rocket);
+
+ $response = $this->buildHtml($rocket->getPayload());
+
+ return $rocket->setDestination($response);
+ }
+
+ protected function buildHtml(Collection $payload): Response
+ {
+ return new Response(200, [], Arr::query($payload->all()));
+ }
+ };
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/CancelShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/CancelShortcut.php
new file mode 100644
index 0000000..752ac23
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/CancelShortcut.php
@@ -0,0 +1,18 @@
+refundPlugins();
+ }
+
+ if (method_exists($this, $typeMethod)) {
+ return $this->{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Query action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ QueryPlugin::class,
+ ];
+ }
+
+ protected function refundPlugins(): array
+ {
+ return [
+ FastRefundQueryPlugin::class,
+ ];
+ }
+
+ protected function transferPlugins(): array
+ {
+ return [
+ TransCommonQueryPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/RefundShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/RefundShortcut.php
new file mode 100644
index 0000000..d1981c2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Shortcut/RefundShortcut.php
@@ -0,0 +1,18 @@
+mergePayload($rocket->getParams());
+ }
+
+ protected function getMethod(): string
+ {
+ return 'alipay.system.oauth.token';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/AppPayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/AppPayPlugin.php
new file mode 100644
index 0000000..250e1aa
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/AppPayPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.app.pay',
+ 'biz_content' => array_merge(
+ ['product_code' => 'QUICK_MSECURITY_PAY'],
+ $rocket->getParams(),
+ ),
+ ])
+ ;
+
+ Logger::info('[alipay][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/CancelPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/CancelPlugin.php
new file mode 100644
index 0000000..40db0b4
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/CancelPlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.page.pay',
+ 'biz_content' => array_merge(
+ ['product_code' => 'FAST_INSTANT_TRADE_PAY'],
+ $rocket->getParams()
+ ),
+ ])
+ ;
+
+ Logger::info('[alipay][PagePayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PageRefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PageRefundPlugin.php
new file mode 100644
index 0000000..3234fbf
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PageRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.page.refund',
+ 'biz_content' => $rocket->getParams(),
+ ])
+ ;
+
+ Logger::info('[alipay][PageRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PayPlugin.php
new file mode 100644
index 0000000..d6a12b9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PayPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'FACE_TO_FACE_PAYMENT',
+ 'scene' => 'bar_code',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[alipay][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PreCreatePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PreCreatePlugin.php
new file mode 100644
index 0000000..f8e4a7f
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/PreCreatePlugin.php
@@ -0,0 +1,33 @@
+loadAlipayServiceProvider($rocket);
+ }
+
+ protected function getMethod(): string
+ {
+ return 'alipay.trade.precreate';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/QueryPlugin.php
new file mode 100644
index 0000000..3c24d50
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/Trade/QueryPlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.wap.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'QUICK_WAP_PAY',
+ ],
+ $rocket->getParams(),
+ ),
+ ])
+ ;
+
+ Logger::info('[alipay][WapPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementExecutionPlanModifyPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementExecutionPlanModifyPlugin.php
new file mode 100644
index 0000000..97abb71
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementExecutionPlanModifyPlugin.php
@@ -0,0 +1,18 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.user.agreement.page.sign',
+ 'biz_content' => array_merge(
+ ['product_code' => 'CYCLE_PAY_AUTH'],
+ $rocket->getParams()
+ ),
+ ])
+ ;
+
+ Logger::info('[alipay][AgreementPageSignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementQueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementQueryPlugin.php
new file mode 100644
index 0000000..590a5a5
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementQueryPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.query',
+ 'biz_content' => array_merge(
+ ['personal_product_code' => 'CYCLE_PAY_AUTH_P'],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[alipay][AgreementQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementTransferPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementTransferPlugin.php
new file mode 100644
index 0000000..60fac81
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementTransferPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.transfer',
+ 'biz_content' => array_merge(
+ ['target_product_code' => 'CYCLE_PAY_AUTH_P'],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[alipay][AgreementTransferPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementUnsignPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementUnsignPlugin.php
new file mode 100644
index 0000000..117cce2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/AgreementUnsignPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.unsign',
+ 'biz_content' => array_merge(
+ ['personal_product_code' => 'CYCLE_PAY_AUTH_P'],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[alipay][AgreementUnsignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/InfoSharePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/InfoSharePlugin.php
new file mode 100644
index 0000000..65de288
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Alipay/User/InfoSharePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.info.share',
+ 'auth_token' => $rocket->getParams()['auth_token'] ?? '',
+ ]);
+
+ Logger::info('[alipay][InfoSharePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/ParserPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/ParserPlugin.php
new file mode 100644
index 0000000..64fbf4f
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/ParserPlugin.php
@@ -0,0 +1,74 @@
+getDestination();
+
+ return $rocket->setDestination(
+ $this->getDirection($rocket)->parse($this->getPacker($rocket), $response)
+ );
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getDirection(Rocket $rocket): DirectionInterface
+ {
+ $packer = Pay::get($rocket->getDirection());
+
+ $packer = is_string($packer) ? Pay::get($packer) : $packer;
+
+ if (!$packer instanceof DirectionInterface) {
+ throw new InvalidConfigException(Exception::INVALID_PARSER);
+ }
+
+ return $packer;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getPacker(Rocket $rocket): PackerInterface
+ {
+ $packer = Pay::get($rocket->getPacker());
+
+ $packer = is_string($packer) ? Pay::get($packer) : $packer;
+
+ if (!$packer instanceof PackerInterface) {
+ throw new InvalidConfigException(Exception::INVALID_PACKER);
+ }
+
+ return $packer;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/CallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/CallbackPlugin.php
new file mode 100644
index 0000000..b234dbd
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/CallbackPlugin.php
@@ -0,0 +1,62 @@
+ $rocket]);
+
+ $this->formatPayload($rocket);
+
+ $params = $rocket->getParams();
+ $signature = $params['signature'] ?? false;
+
+ if (!$signature) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', $params);
+ }
+
+ verify_unipay_sign($params, $rocket->getPayload()->sortKeys()->toString(), $signature);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload())
+ ;
+
+ Logger::info('[unipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function formatPayload(Rocket $rocket): void
+ {
+ $payload = (new Collection($rocket->getParams()))
+ ->filter(fn ($v, $k) => 'signature' != $k && !Str::startsWith($k, '_'))
+ ;
+
+ $rocket->setPayload($payload);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/GeneralPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/GeneralPlugin.php
new file mode 100644
index 0000000..2c53dd9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/GeneralPlugin.php
@@ -0,0 +1,84 @@
+ $rocket]);
+
+ $rocket->setRadar($this->getRequest($rocket));
+ $this->doSomething($rocket);
+
+ Logger::info('[unipay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getRequest(Rocket $rocket): RequestInterface
+ {
+ return new Request(
+ $this->getMethod(),
+ $this->getUrl($rocket),
+ $this->getHeaders(),
+ );
+ }
+
+ protected function getMethod(): string
+ {
+ return 'POST';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUrl(Rocket $rocket): string
+ {
+ $url = $this->getUri($rocket);
+
+ if (0 === strpos($url, 'http')) {
+ return $url;
+ }
+
+ $config = get_unipay_config($rocket->getParams());
+
+ return Unipay::URL[$config['mode'] ?? Pay::MODE_NORMAL].$url;
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'User-Agent' => 'yansongda/pay-v3',
+ 'Content-Type' => 'application/x-www-form-urlencoded;charset=utf-8',
+ ];
+ }
+
+ abstract protected function doSomething(Rocket $rocket): void;
+
+ abstract protected function getUri(Rocket $rocket): string;
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/HtmlResponsePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/HtmlResponsePlugin.php
new file mode 100644
index 0000000..e900e00
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/HtmlResponsePlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $radar = $rocket->getRadar();
+
+ $response = $this->buildHtml($radar->getUri()->__toString(), $rocket->getPayload());
+
+ $rocket->setDestination($response);
+
+ Logger::info('[unipay][HtmlResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function buildHtml(string $endpoint, Collection $payload): Response
+ {
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response(200, [], $sHtml);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/LaunchPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/LaunchPlugin.php
new file mode 100644
index 0000000..563bde3
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/LaunchPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ if (should_do_http_request($rocket->getDirection())) {
+ $response = Collection::wrap($rocket->getDestination());
+ $signature = $response->get('signature');
+ $response->forget('signature');
+
+ verify_unipay_sign(
+ $rocket->getParams(),
+ $response->sortKeys()->toString(),
+ $signature
+ );
+
+ $rocket->setDestination($response);
+ }
+
+ Logger::info('[unipay][LaunchPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/CancelPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/CancelPlugin.php
new file mode 100644
index 0000000..c6397c8
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/CancelPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '31',
+ 'txnSubType' => '00',
+ 'channelType' => '07',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/PagePayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/PagePayPlugin.php
new file mode 100644
index 0000000..2038347
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/PagePayPlugin.php
@@ -0,0 +1,32 @@
+setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'bizType' => '000201',
+ 'txnType' => '01',
+ 'txnSubType' => '01',
+ 'channelType' => '07',
+ ])
+ ;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/QueryPlugin.php
new file mode 100644
index 0000000..c4060f8
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/QueryPlugin.php
@@ -0,0 +1,28 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '00',
+ 'txnSubType' => '00',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/RefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/RefundPlugin.php
new file mode 100644
index 0000000..79c2a1f
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/RefundPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '04',
+ 'txnSubType' => '00',
+ 'channelType' => '07',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/WapPayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/WapPayPlugin.php
new file mode 100644
index 0000000..4048591
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/OnlineGateway/WapPayPlugin.php
@@ -0,0 +1,32 @@
+setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'bizType' => '000201',
+ 'txnType' => '01',
+ 'txnSubType' => '01',
+ 'channelType' => '08',
+ ])
+ ;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/PreparePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/PreparePlugin.php
new file mode 100644
index 0000000..598cfd8
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/PreparePlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $rocket->mergePayload($this->getPayload($rocket->getParams()));
+
+ Logger::info('[unipay][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ */
+ protected function getPayload(array $params): array
+ {
+ $tenant = get_tenant($params);
+ $config = get_unipay_config($params);
+
+ $init = [
+ 'version' => '5.1.0',
+ 'encoding' => 'utf-8',
+ 'backUrl' => $config['notify_url'] ?? '',
+ 'currencyCode' => '156',
+ 'accessType' => '0',
+ 'signature' => '',
+ 'signMethod' => '01',
+ 'merId' => $config['mch_id'] ?? '',
+ 'frontUrl' => $config['return_url'] ?? '',
+ 'certId' => $this->getCertId($tenant, $config),
+ ];
+
+ return array_merge(
+ $init,
+ array_filter($params, fn ($v, $k) => !Str::startsWith(strval($k), '_'), ARRAY_FILTER_USE_BOTH),
+ );
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/CancelPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/CancelPlugin.php
new file mode 100644
index 0000000..80498bf
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/CancelPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '31',
+ 'txnSubType' => '00',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosNormalPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosNormalPlugin.php
new file mode 100644
index 0000000..2c5b8fc
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosNormalPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '01',
+ 'txnSubType' => '06',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosPreAuthPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosPreAuthPlugin.php
new file mode 100644
index 0000000..ff205dc
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/PosPreAuthPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000201',
+ 'txnType' => '02',
+ 'txnSubType' => '04',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/QueryPlugin.php
new file mode 100644
index 0000000..cc82020
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/QueryPlugin.php
@@ -0,0 +1,33 @@
+getParams());
+
+ if (Pay::MODE_SANDBOX === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return 'https://101.231.204.80:5000/gateway/api/backTransReq.do';
+ }
+
+ return parent::getUri($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/RefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/RefundPlugin.php
new file mode 100644
index 0000000..6f7d167
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/RefundPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '04',
+ 'txnSubType' => '00',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanFeePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanFeePlugin.php
new file mode 100644
index 0000000..922b047
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanFeePlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000601',
+ 'txnType' => '13',
+ 'txnSubType' => '08',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanNormalPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanNormalPlugin.php
new file mode 100644
index 0000000..15284ed
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanNormalPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '01',
+ 'txnSubType' => '07',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreAuthPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreAuthPlugin.php
new file mode 100644
index 0000000..559fc63
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreAuthPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '02',
+ 'txnSubType' => '05',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreOrderPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreOrderPlugin.php
new file mode 100644
index 0000000..cd3e461
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/QrCode/ScanPreOrderPlugin.php
@@ -0,0 +1,29 @@
+mergePayload([
+ 'bizType' => '000000',
+ 'txnType' => '01',
+ 'txnSubType' => '01',
+ 'channelType' => '08',
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/RadarSignPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/RadarSignPlugin.php
new file mode 100644
index 0000000..3aa6124
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/RadarSignPlugin.php
@@ -0,0 +1,100 @@
+ $rocket]);
+
+ $this->sign($rocket);
+
+ $this->reRadar($rocket);
+
+ Logger::info('[unipay][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function sign(Rocket $rocket): void
+ {
+ $payload = $rocket->getPayload()->filter(fn ($v, $k) => 'signature' != $k);
+ $config = $this->getConfig($rocket->getParams());
+
+ $rocket->mergePayload([
+ 'signature' => $this->getSignature($config['certs']['pkey'] ?? '', $payload),
+ ]);
+ }
+
+ protected function reRadar(Rocket $rocket): void
+ {
+ $body = $this->getBody($rocket->getPayload());
+ $radar = $rocket->getRadar();
+
+ if (!empty($body) && !empty($radar)) {
+ $radar = $radar->withBody(Utils::streamFor($body));
+
+ $rocket->setRadar($radar);
+ }
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getConfig(array $params): array
+ {
+ $config = get_unipay_config($params);
+
+ if (empty($config['certs']['pkey'])) {
+ $this->getCertId($params['_config'] ?? 'default', $config);
+
+ $config = get_unipay_config($params);
+ }
+
+ return $config;
+ }
+
+ protected function getSignature(string $pkey, Collection $payload): string
+ {
+ $content = $payload->sortKeys()->toString();
+
+ openssl_sign(hash('sha256', $content), $sign, $pkey, 'sha256');
+
+ return base64_encode($sign);
+ }
+
+ protected function getBody(Collection $payload): string
+ {
+ return $payload->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/CancelShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/CancelShortcut.php
new file mode 100644
index 0000000..802d97c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/CancelShortcut.php
@@ -0,0 +1,42 @@
+{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Cancel action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ CancelPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ \Yansongda\Pay\Plugin\Unipay\QrCode\CancelPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/PosShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/PosShortcut.php
new file mode 100644
index 0000000..93dc107
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/PosShortcut.php
@@ -0,0 +1,43 @@
+{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Pos action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ PosNormalPlugin::class,
+ ];
+ }
+
+ protected function preAuthPlugins(): array
+ {
+ return [
+ PosPreAuthPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/QueryShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/QueryShortcut.php
new file mode 100644
index 0000000..a039372
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/QueryShortcut.php
@@ -0,0 +1,42 @@
+{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Query action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ QueryPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ \Yansongda\Pay\Plugin\Unipay\QrCode\QueryPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/RefundShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/RefundShortcut.php
new file mode 100644
index 0000000..862ee6b
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/RefundShortcut.php
@@ -0,0 +1,42 @@
+{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Refund action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ RefundPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ \Yansongda\Pay\Plugin\Unipay\QrCode\RefundPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/ScanShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/ScanShortcut.php
new file mode 100644
index 0000000..a60127d
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/ScanShortcut.php
@@ -0,0 +1,59 @@
+{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Scan action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ ScanNormalPlugin::class,
+ ];
+ }
+
+ protected function preAuthPlugins(): array
+ {
+ return [
+ ScanPreAuthPlugin::class,
+ ];
+ }
+
+ protected function preOrderPlugins(): array
+ {
+ return [
+ ScanPreOrderPlugin::class,
+ ];
+ }
+
+ protected function feePlugins(): array
+ {
+ return [
+ ScanFeePlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/WapShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/WapShortcut.php
new file mode 100644
index 0000000..dcdcc90
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Unipay/Shortcut/WapShortcut.php
@@ -0,0 +1,20 @@
+ $rocket]);
+
+ $this->formatRequestAndParams($rocket);
+
+ /* @phpstan-ignore-next-line */
+ verify_wechat_sign($rocket->getDestinationOrigin(), $rocket->getParams());
+
+ $body = json_decode((string) $rocket->getDestination()->getBody(), true);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)->setPayload(new Collection($body));
+
+ $body['resource'] = decrypt_wechat_resource($body['resource'] ?? [], $rocket->getParams());
+
+ $rocket->setDestination(new Collection($body));
+
+ Logger::info('[wechat][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function formatRequestAndParams(Rocket $rocket): void
+ {
+ $request = $rocket->getParams()['request'] ?? null;
+
+ if (!$request instanceof ServerRequestInterface) {
+ throw new InvalidParamsException(Exception::REQUEST_NULL_ERROR);
+ }
+
+ $rocket->setDestination(clone $request)
+ ->setDestinationOrigin($request)
+ ->setParams($rocket->getParams()['params'] ?? [])
+ ;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ApplyPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ApplyPlugin.php
new file mode 100644
index 0000000..64967dc
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ApplyPlugin.php
@@ -0,0 +1,55 @@
+getParams());
+ $payload = $rocket->getPayload();
+ $key = $this->getConfigKey($rocket->getParams());
+
+ $wechatId = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'sp_appid' => $payload->get('sp_appid', $config[$key] ?? ''),
+ ];
+
+ if (!$payload->has('notify_url')) {
+ $wechatId['notify_url'] = $config['notify_url'] ?? null;
+ }
+
+ $rocket->mergePayload($wechatId);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryPlugin.php
new file mode 100644
index 0000000..7a1cca1
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryPlugin.php
@@ -0,0 +1,60 @@
+getPayload();
+ $config = get_wechat_config($rocket->getParams());
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ if ($payload->has('refund_id')) {
+ return 'v3/ecommerce/refunds/id/'.$payload->get('refund_id').'?sub_mchid='.$subMchId;
+ }
+
+ if ($payload->has('out_refund_no')) {
+ return 'v3/ecommerce/refunds/out-refund-no/'.$payload->get('out_refund_no').'?sub_mchid='.$subMchId;
+ }
+
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryReturnAdvancePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryReturnAdvancePlugin.php
new file mode 100644
index 0000000..503e440
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/QueryReturnAdvancePlugin.php
@@ -0,0 +1,56 @@
+getPayload();
+ $config = get_wechat_config($rocket->getParams());
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ if (!$payload->has('refund_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/ecommerce/refunds/'.$payload->get('refund_id').'/return-advance?sub_mchid='.$subMchId;
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ReturnAdvancePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ReturnAdvancePlugin.php
new file mode 100644
index 0000000..8270b2e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Ecommerce/Refund/ReturnAdvancePlugin.php
@@ -0,0 +1,56 @@
+getPayload();
+
+ if (!$payload->has('refund_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/ecommerce/refunds/'.$payload->get('refund_id').'/return-advance';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function doSomething(Rocket $rocket): void
+ {
+ $config = get_wechat_config($rocket->getParams());
+
+ $rocket->setPayload(new Collection([
+ 'sub_mchid' => $rocket->getPayload()->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]));
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryDayEndPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryDayEndPlugin.php
new file mode 100644
index 0000000..7964773
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryDayEndPlugin.php
@@ -0,0 +1,38 @@
+getPayload();
+
+ if (!$payload->has('account_type') || !$payload->has('date')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant/fund/dayendbalance/'.
+ $payload->get('account_type').
+ '?date='.$payload->get('date');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryPlugin.php
new file mode 100644
index 0000000..9e10e5d
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Balance/QueryPlugin.php
@@ -0,0 +1,37 @@
+getPayload();
+
+ if (!$payload->has('account_type')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant/fund/balance/'.
+ $payload->get('account_type');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/AddReceiverPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/AddReceiverPlugin.php
new file mode 100644
index 0000000..6510ca5
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/AddReceiverPlugin.php
@@ -0,0 +1,83 @@
+getParams();
+ $config = get_wechat_config($rocket->getParams());
+ $extra = $this->getWechatId($config, $rocket->getPayload());
+
+ if (!empty($params['name'] ?? '')) {
+ $params = $this->loadSerialNo($params);
+
+ $name = $this->getEncryptUserName($params);
+ $params['name'] = $name;
+ $extra['name'] = $name;
+ $rocket->setParams($params);
+ }
+
+ $rocket->mergePayload($extra);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/profitsharing/receivers/add';
+ }
+
+ protected function getWechatId(array $config, Collection $payload): array
+ {
+ $wechatId = [
+ 'appid' => $config['mp_app_id'] ?? null,
+ ];
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $wechatId['sub_mchid'] = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+
+ return $wechatId;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getEncryptUserName(array $params): string
+ {
+ $name = $params['name'] ?? '';
+ $publicKey = $this->getPublicKey($params, $params['_serial_no'] ?? '');
+
+ return encrypt_wechat_contents($name, $publicKey);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/CreatePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/CreatePlugin.php
new file mode 100644
index 0000000..9f4cc66
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/CreatePlugin.php
@@ -0,0 +1,89 @@
+getPayload();
+ $params = $this->loadSerialNo($rocket->getParams());
+
+ $extra = $this->getWechatExtra($params, $payload);
+ $extra['receivers'] = $this->getReceivers($params);
+
+ $rocket->setParams($params);
+ $rocket->mergePayload($extra);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/profitsharing/orders';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getWechatExtra(array $params, Collection $payload): array
+ {
+ $config = get_wechat_config($params);
+
+ $extra = [
+ 'appid' => $config['mp_app_id'] ?? null,
+ ];
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $extra['sub_mchid'] = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+
+ return $extra;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getReceivers(array $params): array
+ {
+ $publicKey = $this->getPublicKey($params, $params['_serial_no'] ?? '');
+ $receivers = $params['receivers'] ?? [];
+
+ foreach ($receivers as $key => $receiver) {
+ if (!empty($receiver['name'])) {
+ $receivers[$key]['name'] = encrypt_wechat_contents($receiver['name'], $publicKey);
+ }
+ }
+
+ return $receivers;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DeleteReceiverPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DeleteReceiverPlugin.php
new file mode 100644
index 0000000..9ea3ae1
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DeleteReceiverPlugin.php
@@ -0,0 +1,45 @@
+getParams());
+
+ $wechatId = [
+ 'appid' => $config['mp_app_id'] ?? null,
+ ];
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $wechatId['sub_mchid'] = $rocket->getPayload()
+ ->get('sub_mchid', $config['sub_mch_id'] ?? '')
+ ;
+ }
+
+ $rocket->mergePayload($wechatId);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/profitsharing/receivers/delete';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DownloadBillPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DownloadBillPlugin.php
new file mode 100644
index 0000000..8fa9170
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/DownloadBillPlugin.php
@@ -0,0 +1,43 @@
+getPayload();
+
+ if (!$payload->has('download_url')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return $payload->get('download_url');
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setDirection(OriginResponseDirection::class);
+
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryAmountsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryAmountsPlugin.php
new file mode 100644
index 0000000..7bd73c7
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryAmountsPlugin.php
@@ -0,0 +1,42 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('transaction_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/profitsharing/transactions/'.
+ $payload->get('transaction_id').
+ '/amounts';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryMerchantConfigsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryMerchantConfigsPlugin.php
new file mode 100644
index 0000000..06df349
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryMerchantConfigsPlugin.php
@@ -0,0 +1,49 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $config = get_wechat_config($rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ if (Pay::MODE_SERVICE !== ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::METHOD_NOT_SUPPORTED);
+ }
+
+ return 'v3/profitsharing/merchant-configs/'.
+ $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryPlugin.php
new file mode 100644
index 0000000..141693c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryPlugin.php
@@ -0,0 +1,56 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $config = get_wechat_config($rocket->getParams());
+
+ if (!$payload->has('out_order_no') || !$payload->has('transaction_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $url = 'v3/profitsharing/orders/'.
+ $payload->get('out_order_no').
+ '?transaction_id='.$payload->get('transaction_id');
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $url .= '&sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+
+ return $url;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryReturnPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryReturnPlugin.php
new file mode 100644
index 0000000..a3e4888
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/QueryReturnPlugin.php
@@ -0,0 +1,56 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $config = get_wechat_config($rocket->getParams());
+
+ if (!$payload->has('out_return_no') || !$payload->has('out_order_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $url = 'v3/profitsharing/return-orders/'.
+ $payload->get('out_return_no').
+ '?out_order_no='.$payload->get('out_order_no');
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $url .= '&sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+
+ return $url;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/ReturnPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/ReturnPlugin.php
new file mode 100644
index 0000000..5e0f37a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/ReturnPlugin.php
@@ -0,0 +1,40 @@
+getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $rocket->mergePayload([
+ 'sub_mchid' => $rocket->getPayload()
+ ->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/profitsharing/return-orders';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/UnfreezePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/UnfreezePlugin.php
new file mode 100644
index 0000000..5ccb837
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Profitsharing/UnfreezePlugin.php
@@ -0,0 +1,40 @@
+getPayload();
+ $config = get_wechat_config($rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null) && !$payload->has('sub_mchid')) {
+ $rocket->mergePayload([
+ 'sub_mchid' => $config['sub_mch_id'] ?? '',
+ ]);
+ }
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/profitsharing/orders/unfreeze';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateBillReceiptPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateBillReceiptPlugin.php
new file mode 100644
index 0000000..add6ce2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateBillReceiptPlugin.php
@@ -0,0 +1,33 @@
+getPayload();
+
+ if (!$payload->has('out_batch_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/transfer/bill-receipt';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateDetailReceiptPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateDetailReceiptPlugin.php
new file mode 100644
index 0000000..882b376
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreateDetailReceiptPlugin.php
@@ -0,0 +1,33 @@
+getPayload();
+
+ if (!$payload->has('out_detail_no') || !$payload->has('accept_type')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/transfer-detail/electronic-receipts';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreatePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreatePlugin.php
new file mode 100644
index 0000000..a8459a4
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/CreatePlugin.php
@@ -0,0 +1,99 @@
+getParams();
+ $extra = $this->getWechatId($params, $rocket->getPayload());
+
+ if (!empty($params['transfer_detail_list'][0]['user_name'] ?? '')) {
+ $params = $this->loadSerialNo($params);
+
+ $rocket->setParams($params);
+
+ $extra['transfer_detail_list'] = $this->getEncryptUserName($params);
+ }
+
+ $rocket->mergePayload($extra);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/transfer/batches';
+ }
+
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ return 'v3/partner-transfer/batches';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getWechatId(array $params, Collection $payload): array
+ {
+ $config = get_wechat_config($params);
+ $key = $this->getConfigKey($params);
+
+ $appId = [
+ 'appid' => $payload->get('appid', $config[$key] ?? ''),
+ ];
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $appId = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+
+ return $appId;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getEncryptUserName(array $params): array
+ {
+ $lists = $params['transfer_detail_list'] ?? [];
+ $publicKey = $this->getPublicKey($params, $params['_serial_no'] ?? '');
+
+ foreach ($lists as $key => $list) {
+ $lists[$key]['user_name'] = encrypt_wechat_contents($list['user_name'], $publicKey);
+ }
+
+ return $lists;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/DownloadReceiptPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/DownloadReceiptPlugin.php
new file mode 100644
index 0000000..1bd5025
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/DownloadReceiptPlugin.php
@@ -0,0 +1,43 @@
+getPayload();
+
+ if (!$payload->has('download_url')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return $payload->get('download_url');
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setDirection(OriginResponseDirection::class);
+
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchDetailIdPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchDetailIdPlugin.php
new file mode 100644
index 0000000..b2251dd
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchDetailIdPlugin.php
@@ -0,0 +1,60 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('batch_id') || !$payload->get('detail_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/transfer/batches/batch-id/'.
+ $payload->get('batch_id').
+ '/details/detail-id/'.
+ $payload->get('detail_id');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('batch_id') || !$payload->has('detail_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/partner-transfer/batches/batch-id/'.
+ $payload->get('batch_id').
+ '/details/detail-id/'.
+ $payload->get('detail_id');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchIdPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchIdPlugin.php
new file mode 100644
index 0000000..c87a4f2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBatchIdPlugin.php
@@ -0,0 +1,64 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('batch_id') || !$payload->has('need_query_detail')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $batchId = $payload->get('batch_id');
+
+ $payload->forget('batch_id');
+
+ return 'v3/transfer/batches/batch-id/'.$batchId.
+ '?'.$payload->query();
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('batch_id') || !$payload->has('need_query_detail')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $batchId = $payload->get('batch_id');
+
+ $payload->forget('batch_id');
+
+ return 'v3/partner-transfer/batches/batch-id/'.$batchId.
+ '?'.$payload->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBillReceiptPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBillReceiptPlugin.php
new file mode 100644
index 0000000..5fdd9da
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryBillReceiptPlugin.php
@@ -0,0 +1,40 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_batch_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/transfer/bill-receipt/'.$payload->get('out_batch_no');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryDetailReceiptPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryDetailReceiptPlugin.php
new file mode 100644
index 0000000..39aa13f
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryDetailReceiptPlugin.php
@@ -0,0 +1,40 @@
+getPayload();
+
+ if (!$payload->has('out_detail_no') || !$payload->has('accept_type')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $rocket->setPayload(null);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/transfer-detail/electronic-receipts?'.$rocket->getPayload()->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchDetailNoPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchDetailNoPlugin.php
new file mode 100644
index 0000000..199430b
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchDetailNoPlugin.php
@@ -0,0 +1,60 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_batch_no') || !$payload->has('out_detail_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/transfer/batches/out-batch-no/'.
+ $payload->get('out_batch_no').
+ '/details/out-detail-no/'.
+ $payload->get('out_detail_no');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_batch_no') || !$payload->has('out_detail_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/partner-transfer/batches/out-batch-no/'.
+ $payload->get('out_batch_no').
+ '/details/out-detail-no/'.
+ $payload->get('out_detail_no');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchNoPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchNoPlugin.php
new file mode 100644
index 0000000..ec5a191
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Fund/Transfer/QueryOutBatchNoPlugin.php
@@ -0,0 +1,64 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_batch_no') || !$payload->has('need_query_detail')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $outBatchNo = $payload->get('out_batch_no');
+
+ $payload->forget('out_batch_no');
+
+ return 'v3/transfer/batches/out-batch-no/'.$outBatchNo.
+ '?'.$payload->query();
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_batch_no') || !$payload->has('need_query_detail')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $outBatchNo = $payload->get('out_batch_no');
+
+ $payload->forget('out_batch_no');
+
+ return 'v3/partner-transfer/batches/out-batch-no/'.$outBatchNo.
+ '?'.$payload->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralPlugin.php
new file mode 100644
index 0000000..82e8702
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralPlugin.php
@@ -0,0 +1,97 @@
+ $rocket]);
+
+ $rocket->setRadar($this->getRequest($rocket));
+ $this->doSomething($rocket);
+
+ Logger::info('[wechat][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getRequest(Rocket $rocket): RequestInterface
+ {
+ return new Request(
+ $this->getMethod(),
+ $this->getUrl($rocket),
+ $this->getHeaders(),
+ );
+ }
+
+ protected function getMethod(): string
+ {
+ return 'POST';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUrl(Rocket $rocket): string
+ {
+ $params = $rocket->getParams();
+
+ $url = Pay::MODE_SERVICE === (get_wechat_config($params)['mode'] ?? null) ? $this->getPartnerUri($rocket) : $this->getUri($rocket);
+
+ return 0 === strpos($url, 'http') ? $url : (get_wechat_base_uri($params).$url);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'Accept' => 'application/json, text/plain, application/x-gzip',
+ 'User-Agent' => 'yansongda/pay-v3',
+ 'Content-Type' => 'application/json; charset=utf-8',
+ ];
+ }
+
+ protected function getConfigKey(array $params): string
+ {
+ $key = ($params['_type'] ?? 'mp').'_app_id';
+
+ if ('app_app_id' === $key) {
+ $key = 'app_id';
+ }
+
+ return $key;
+ }
+
+ abstract protected function doSomething(Rocket $rocket): void;
+
+ abstract protected function getUri(Rocket $rocket): string;
+
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ return $this->getUri($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralV2Plugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralV2Plugin.php
new file mode 100644
index 0000000..ce6713d
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/GeneralV2Plugin.php
@@ -0,0 +1,42 @@
+ 'application/xml',
+ 'User-Agent' => 'yansongda/pay-v3',
+ ];
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function doSomething(Rocket $rocket): void
+ {
+ $config = get_wechat_config($rocket->getParams());
+ $configKey = $this->getConfigKey($rocket->getParams());
+
+ $rocket->setPacker(XmlPacker::class)->mergeParams(['_version' => 'v2']);
+
+ $rocket->mergePayload([
+ 'appid' => $config[$configKey] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ abstract protected function getUri(Rocket $rocket): string;
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/LaunchPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/LaunchPlugin.php
new file mode 100644
index 0000000..68e3983
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/LaunchPlugin.php
@@ -0,0 +1,67 @@
+ $rocket]);
+
+ if (should_do_http_request($rocket->getDirection())) {
+ verify_wechat_sign($rocket->getDestinationOrigin(), $rocket->getParams());
+
+ $rocket->setDestination($this->validateResponse($rocket));
+ }
+
+ Logger::info('[wechat][LaunchPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ *
+ * @throws InvalidResponseException
+ */
+ protected function validateResponse(Rocket $rocket)
+ {
+ $response = $rocket->getDestination();
+
+ if ($response instanceof ResponseInterface
+ && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300)) {
+ throw new InvalidResponseException(Exception::INVALID_RESPONSE_CODE);
+ }
+
+ return $response;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/CreatePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/CreatePlugin.php
new file mode 100644
index 0000000..d5bc8c2
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/CreatePlugin.php
@@ -0,0 +1,37 @@
+getParams());
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('belong_merchant')) {
+ $rocket->mergePayload(['belong_merchant' => $config['mch_id']]);
+ }
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/marketing/favor/coupon-stocks';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/PausePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/PausePlugin.php
new file mode 100644
index 0000000..4e27fe1
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/PausePlugin.php
@@ -0,0 +1,50 @@
+getParams());
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ $rocket->getPayload()->forget('stock_id');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.$payload->get('stock_id').'/pause';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryCouponDetailPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryCouponDetailPlugin.php
new file mode 100644
index 0000000..c05a923
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryCouponDetailPlugin.php
@@ -0,0 +1,51 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('coupon_id') || !$payload->has('openid')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/users/'.
+ $payload->get('openid').
+ '/coupons/'.$payload->get('coupon_id').
+ '?appid='.$payload->get('appid', $config[$this->getConfigKey($params)] ?? '');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockDetailPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockDetailPlugin.php
new file mode 100644
index 0000000..d21d836
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockDetailPlugin.php
@@ -0,0 +1,50 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.
+ $payload->get('stock_id').
+ '?stock_creator_mchid='.$payload->get('stock_creator_mchid', $config['mch_id'] ?? '');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockItemsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockItemsPlugin.php
new file mode 100644
index 0000000..eefe524
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockItemsPlugin.php
@@ -0,0 +1,58 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ if (!$payload->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ $query = $rocket->getPayload()->all();
+
+ unset($query['stock_id']);
+
+ return 'v3/marketing/favor/stocks/'.
+ $payload->get('stock_id').
+ '/items?'.http_build_query($query);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockMerchantsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockMerchantsPlugin.php
new file mode 100644
index 0000000..9f3278b
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockMerchantsPlugin.php
@@ -0,0 +1,58 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $query = $rocket->getPayload()->all();
+
+ unset($query['stock_id']);
+
+ return 'v3/marketing/favor/stocks/'.
+ $payload->get('stock_id').
+ '/merchants?'.http_build_query($query);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockRefundFlowPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockRefundFlowPlugin.php
new file mode 100644
index 0000000..baff7ac
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockRefundFlowPlugin.php
@@ -0,0 +1,42 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.
+ $payload->get('stock_id').
+ '/refund-flow';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockUseFlowPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockUseFlowPlugin.php
new file mode 100644
index 0000000..a639158
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStockUseFlowPlugin.php
@@ -0,0 +1,42 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.
+ $payload->get('stock_id').
+ '/use-flow';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStocksPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStocksPlugin.php
new file mode 100644
index 0000000..700c9a4
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryStocksPlugin.php
@@ -0,0 +1,44 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$rocket->getPayload()->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ return 'v3/marketing/favor/stocks?'.$rocket->getPayload()->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryUserCouponsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryUserCouponsPlugin.php
new file mode 100644
index 0000000..80496c0
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/QueryUserCouponsPlugin.php
@@ -0,0 +1,62 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('openid')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ if (!$payload->has('appid')) {
+ $rocket->mergePayload(['appid' => $config[$this->getConfigKey($params)] ?? '']);
+ }
+
+ if (!$payload->has('creator_mchid')) {
+ $rocket->mergePayload(['creator_mchid' => $config['mch_id']]);
+ }
+
+ $query = $rocket->getPayload()->all();
+
+ unset($query['openid']);
+
+ return 'v3/marketing/favor/users/'.
+ $payload->get('openid').
+ '/coupons?'.http_build_query($query);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/RestartPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/RestartPlugin.php
new file mode 100644
index 0000000..602956a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/RestartPlugin.php
@@ -0,0 +1,51 @@
+getPayload();
+ $params = $rocket->getParams();
+ $config = get_wechat_config($params);
+
+ if (!$payload->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ $rocket->getPayload()->forget('stock_id');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.$payload->get('stock_id').'/restart';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SendPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SendPlugin.php
new file mode 100644
index 0000000..cf9017e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SendPlugin.php
@@ -0,0 +1,54 @@
+getParams();
+ $config = get_wechat_config($params);
+
+ if (!$rocket->getPayload()->has('appid')) {
+ $rocket->mergePayload(['appid' => $config[$this->getConfigKey($params)] ?? '']);
+ }
+
+ if (!$rocket->getPayload()->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ $rocket->getPayload()->forget('openid');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('openid')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/users/'.$payload->get('openid').'/coupons';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SetCallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SetCallbackPlugin.php
new file mode 100644
index 0000000..e620c1d
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/SetCallbackPlugin.php
@@ -0,0 +1,36 @@
+getParams());
+
+ $rocket->mergePayload([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/marketing/favor/callbacks';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/StartPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/StartPlugin.php
new file mode 100644
index 0000000..f26ad4c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Marketing/Coupon/StartPlugin.php
@@ -0,0 +1,50 @@
+getParams();
+ $config = get_wechat_config($params);
+
+ if (!$rocket->getPayload()->has('stock_creator_mchid')) {
+ $rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
+ }
+
+ $rocket->getPayload()->forget('stock_id');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('stock_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/marketing/favor/stocks/'.$payload->get('stock_id').'/start';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Papay/ApplyPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Papay/ApplyPlugin.php
new file mode 100644
index 0000000..5443b00
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Papay/ApplyPlugin.php
@@ -0,0 +1,21 @@
+getParams());
+ $wechatId = $this->getWechatId($config, $rocket->getParams());
+
+ if (!$rocket->getPayload()->has('notify_url')) {
+ $wechatId['notify_url'] = $config['notify_url'] ?? '';
+ }
+
+ $rocket->mergePayload($wechatId);
+ $rocket->mergePayload([
+ 'sign' => get_wechat_sign_v2($rocket->getParams(), $rocket->getPayload()->all()),
+ ]);
+
+ $rocket->setDestination($rocket->getPayload());
+ $rocket->setDirection(NoHttpRequestDirection::class);
+
+ return $next($rocket);
+ }
+
+ protected function getWechatId(array $config, array $params): array
+ {
+ $configKey = $this->getConfigKey($params);
+
+ return [
+ 'appid' => $config[$configKey] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function getConfigKey(array $params): string
+ {
+ $key = ($params['_type'] ?? 'mp').'_app_id';
+
+ if ('app_app_id' === $key) {
+ $key = 'app_id';
+ }
+
+ return $key;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/ClosePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/ClosePlugin.php
new file mode 100644
index 0000000..6740c31
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/ClosePlugin.php
@@ -0,0 +1,12 @@
+ $this->getAppId($rocket),
+ 'partnerid' => get_wechat_config($rocket->getParams())['mch_id'] ?? null,
+ 'prepayid' => $prepayId,
+ 'package' => 'Sign=WXPay',
+ 'noncestr' => Str::random(32),
+ 'timestamp' => time().'',
+ ]);
+
+ $config->set('sign', $this->getSign($config, $rocket->getParams()));
+
+ return $config;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getSign(Collection $invokeConfig, array $params): string
+ {
+ $contents = $invokeConfig->get('appid', '')."\n".
+ $invokeConfig->get('timestamp', '')."\n".
+ $invokeConfig->get('noncestr', '')."\n".
+ $invokeConfig->get('prepayid', '')."\n";
+
+ return get_wechat_sign($params, $contents);
+ }
+
+ protected function getConfigKey(): string
+ {
+ return 'app_id';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/InvokePrepayV2Plugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/InvokePrepayV2Plugin.php
new file mode 100644
index 0000000..8ec4ba9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/InvokePrepayV2Plugin.php
@@ -0,0 +1,49 @@
+getParams();
+
+ $config = new Config([
+ 'appId' => $this->getAppId($rocket),
+ 'partnerId' => get_wechat_config($params)['mch_id'] ?? null,
+ 'prepayId' => $prepayId,
+ 'package' => 'Sign=WXPay',
+ 'nonceStr' => Str::random(32),
+ 'timeStamp' => time().'',
+ ]);
+
+ $config->set('sign', get_wechat_sign_v2($params, $config->all()));
+
+ return $config;
+ }
+
+ protected function getConfigKey(): string
+ {
+ return 'app_id';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/PrepayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/PrepayPlugin.php
new file mode 100644
index 0000000..12df20e
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/App/PrepayPlugin.php
@@ -0,0 +1,28 @@
+getPayload();
+
+ if (!$payload->has('combine_out_trade_no') && !$payload->has('out_trade_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/combine-transactions/out-trade-no/'.
+ $payload->get('combine_out_trade_no', $payload->get('out_trade_no')).
+ '/close';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function doSomething(Rocket $rocket): void
+ {
+ $config = get_wechat_config($rocket->getParams());
+
+ $rocket->setPayload(new Collection([
+ 'combine_appid' => $config['combine_appid'] ?? '',
+ 'sub_orders' => $rocket->getParams()['sub_orders'] ?? [],
+ ]));
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/DownloadBillPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/DownloadBillPlugin.php
new file mode 100644
index 0000000..2d17451
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/DownloadBillPlugin.php
@@ -0,0 +1,12 @@
+getPayload();
+
+ if (!$payload->has('combine_out_trade_no') && !$payload->has('transaction_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/combine-transactions/out-trade-no/'.
+ $payload->get('combine_out_trade_no', $payload->get('transaction_id'));
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/QueryRefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/QueryRefundPlugin.php
new file mode 100644
index 0000000..16ba93a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Combine/QueryRefundPlugin.php
@@ -0,0 +1,12 @@
+getPayload();
+
+ if (!$payload->has('out_trade_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/pay/transactions/out-trade-no/'.
+ $payload->get('out_trade_no').
+ '/close';
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('out_trade_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/pay/partner/transactions/out-trade-no/'.
+ $payload->get('out_trade_no').
+ '/close';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setDirection(OriginResponseDirection::class);
+
+ $config = get_wechat_config($rocket->getParams());
+
+ $body = [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+
+ if (Pay::MODE_SERVICE == ($config['mode'] ?? null)) {
+ $body = [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $rocket->getPayload()->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+
+ $rocket->setPayload(new Collection($body));
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/CombinePrepayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/CombinePrepayPlugin.php
new file mode 100644
index 0000000..24363da
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/CombinePrepayPlugin.php
@@ -0,0 +1,50 @@
+getParams());
+ $collection = $rocket->getPayload();
+
+ $payload = $this->getWechatId($config);
+
+ if (!$collection->has('notify_url')) {
+ $payload['notify_url'] = $config['notify_url'] ?? '';
+ }
+
+ if (!$collection->has('combine_out_trade_no')) {
+ $payload['combine_out_trade_no'] = $rocket->getParams()['out_trade_no'];
+ }
+
+ $rocket->mergePayload($payload);
+ }
+
+ protected function getWechatId(array $config): array
+ {
+ return [
+ 'combine_appid' => $config['combine_app_id'] ?? '',
+ 'combine_mchid' => $config['combine_mch_id'] ?? '',
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/DownloadBillPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/DownloadBillPlugin.php
new file mode 100644
index 0000000..a7651d7
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/DownloadBillPlugin.php
@@ -0,0 +1,40 @@
+getPayload();
+
+ if (!$payload->has('download_url')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return $payload->get('download_url');
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setDirection(OriginResponseDirection::class);
+
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetFlowBillPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetFlowBillPlugin.php
new file mode 100644
index 0000000..a170038
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetFlowBillPlugin.php
@@ -0,0 +1,26 @@
+getParams());
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetTradeBillPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetTradeBillPlugin.php
new file mode 100644
index 0000000..692e2cf
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/GetTradeBillPlugin.php
@@ -0,0 +1,26 @@
+getParams());
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayPlugin.php
new file mode 100644
index 0000000..0de41e9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayPlugin.php
@@ -0,0 +1,110 @@
+ $rocket]);
+
+ $prepayId = $rocket->getDestination()->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[wechat][InvokePrepayPlugin] 预下单失败:响应缺少 prepay_id 参数,请自行检查参数是否符合微信要求', $rocket->getDestination()->all());
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, 'Prepay Response Error: Missing PrepayId', $rocket->getDestination()->all());
+ }
+
+ $config = $this->getInvokeConfig($rocket, $prepayId);
+
+ $rocket->setDestination($config);
+
+ Logger::info('[wechat][InvokePrepayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws \Exception
+ */
+ protected function getInvokeConfig(Rocket $rocket, string $prepayId): Config
+ {
+ $config = new Config([
+ 'appId' => $this->getAppId($rocket),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'RSA',
+ ]);
+
+ $config->set('paySign', $this->getSign($config, $rocket->getParams()));
+
+ return $config;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getSign(Collection $invokeConfig, array $params): string
+ {
+ $contents = $invokeConfig->get('appId', '')."\n".
+ $invokeConfig->get('timeStamp', '')."\n".
+ $invokeConfig->get('nonceStr', '')."\n".
+ $invokeConfig->get('package', '')."\n";
+
+ return get_wechat_sign($params, $contents);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getAppId(Rocket $rocket): string
+ {
+ $config = get_wechat_config($rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null) && $payload->has('sub_appid')) {
+ return $payload->get('sub_appid', '');
+ }
+
+ return $config[$this->getConfigKey()] ?? '';
+ }
+
+ protected function getConfigKey(): string
+ {
+ return 'mp_app_id';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayV2Plugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayV2Plugin.php
new file mode 100644
index 0000000..1c4313c
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/InvokePrepayV2Plugin.php
@@ -0,0 +1,40 @@
+ $this->getAppId($rocket),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'MD5',
+ ]);
+
+ $config->set('paySign', get_wechat_sign_v2($rocket->getParams(), $config->toArray()));
+
+ return $config;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/PrepayPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/PrepayPlugin.php
new file mode 100644
index 0000000..5ffaa2a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/PrepayPlugin.php
@@ -0,0 +1,70 @@
+getParams());
+
+ $wechatId = $this->getWechatId($config, $rocket);
+
+ if (!$rocket->getPayload()->has('notify_url')) {
+ $wechatId['notify_url'] = $config['notify_url'] ?? null;
+ }
+
+ $rocket->mergePayload($wechatId);
+ }
+
+ protected function getWechatId(array $config, Rocket $rocket): array
+ {
+ $payload = $rocket->getPayload();
+ $configKey = $this->getConfigKey($rocket->getParams());
+
+ $result = [
+ 'appid' => $config[$configKey] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $result = [
+ 'sp_appid' => $config[$configKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? null),
+ ];
+
+ $subAppId = $payload->get('sub_appid', $config['sub_'.$configKey] ?? null);
+
+ if (!empty($subAppId)) {
+ $result['sub_appid'] = $subAppId;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryPlugin.php
new file mode 100644
index 0000000..68071c7
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryPlugin.php
@@ -0,0 +1,79 @@
+getParams());
+ $payload = $rocket->getPayload();
+
+ if ($payload->has('transaction_id')) {
+ return 'v3/pay/transactions/id/'.
+ $payload->get('transaction_id').
+ '?mchid='.($config['mch_id'] ?? '');
+ }
+
+ if ($payload->has('out_trade_no')) {
+ return 'v3/pay/transactions/out-trade-no/'.
+ $payload->get('out_trade_no').
+ '?mchid='.($config['mch_id'] ?? '');
+ }
+
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidParamsException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $config = get_wechat_config($rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ if ($payload->has('transaction_id')) {
+ return 'v3/pay/partner/transactions/id/'.
+ $payload->get('transaction_id').
+ '?sp_mchid='.($config['mch_id'] ?? '').
+ '&sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? null);
+ }
+
+ if ($payload->has('out_trade_no')) {
+ return 'v3/pay/partner/transactions/out-trade-no/'.
+ $payload->get('out_trade_no').
+ '?sp_mchid='.($config['mch_id'] ?? '').
+ '&sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? null);
+ }
+
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryRefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryRefundPlugin.php
new file mode 100644
index 0000000..e30df72
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/QueryRefundPlugin.php
@@ -0,0 +1,53 @@
+getPayload();
+
+ if (!$payload->has('out_refund_no')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/refund/domestic/refunds/'.$payload->get('out_refund_no');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getPartnerUri(Rocket $rocket): string
+ {
+ $config = get_wechat_config($rocket->getParams());
+ $url = parent::getPartnerUri($rocket);
+
+ return $url.'?sub_mchid='.$rocket->getPayload()->get('sub_mchid', $config['sub_mch_id'] ?? '');
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/RefundPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/RefundPlugin.php
new file mode 100644
index 0000000..3be79d0
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/Common/RefundPlugin.php
@@ -0,0 +1,41 @@
+getParams());
+ $payload = $rocket->getPayload();
+
+ if (empty($payload->get('notify_url')) && !empty($config['notify_url'])) {
+ $merge['notify_url'] = $config['notify_url'];
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? null)) {
+ $merge['sub_mchid'] = $payload->get('sub_mchid', $config['sub_mch_id'] ?? null);
+ }
+
+ $rocket->mergePayload($merge ?? []);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/H5/ClosePlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/H5/ClosePlugin.php
new file mode 100644
index 0000000..9d805cf
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Pay/H5/ClosePlugin.php
@@ -0,0 +1,12 @@
+ $rocket]);
+
+ $rocket->mergePayload($this->getPayload($rocket->getParams()));
+
+ Logger::info('[wechat][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getPayload(array $params): array
+ {
+ return array_filter($params, fn ($v, $k) => !Str::startsWith(strval($k), '_'), ARRAY_FILTER_USE_BOTH);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/RadarSignPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/RadarSignPlugin.php
new file mode 100644
index 0000000..87faad6
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/RadarSignPlugin.php
@@ -0,0 +1,170 @@
+jsonPacker = $jsonPacker ?? new JsonPacker();
+ $this->xmlPacker = $xmlPacker ?? new XmlPacker();
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function assembly(Rocket $rocket, Closure $next): Rocket
+ {
+ Logger::debug('[wechat][RadarSignPlugin] 插件开始装载', ['rocket' => $rocket]);
+
+ switch ($rocket->getParams()['_version'] ?? 'default') {
+ case 'v2':
+ $radar = $this->v2($rocket);
+
+ break;
+
+ default:
+ $radar = $this->v3($rocket);
+
+ break;
+ }
+
+ $rocket->setRadar($radar);
+
+ Logger::info('[wechat][RadarSignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ * @throws \Exception
+ */
+ protected function v2(Rocket $rocket): RequestInterface
+ {
+ $rocket->mergePayload(['nonce_str' => Str::random(32)]);
+ $rocket->mergePayload([
+ 'sign' => get_wechat_sign_v2($rocket->getParams(), $rocket->getPayload()->all()),
+ ]);
+
+ return $rocket->getRadar()->withBody(
+ Utils::streamFor($this->xmlPacker->pack($rocket->getPayload()->all()))
+ );
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ * @throws \Exception
+ */
+ protected function v3(Rocket $rocket): RequestInterface
+ {
+ $timestamp = time();
+ $random = Str::random(32);
+ $body = $this->v3PayloadToString($rocket->getPayload());
+ $contents = $this->v3GetContents($rocket, $timestamp, $random);
+ $authorization = $this->v3GetWechatAuthorization($rocket->getParams(), $timestamp, $random, $contents);
+ $radar = $rocket->getRadar()->withHeader('Authorization', $authorization);
+
+ if (!empty($rocket->getParams()['_serial_no'])) {
+ $radar = $radar->withHeader('Wechatpay-Serial', $rocket->getParams()['_serial_no']);
+ }
+
+ if (!empty($body)) {
+ $radar = $radar->withBody(Utils::streamFor($body));
+ }
+
+ return $radar;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function v3GetWechatAuthorization(array $params, int $timestamp, string $random, string $contents): string
+ {
+ $config = get_wechat_config($params);
+ $mchPublicCertPath = $config['mch_public_cert_path'] ?? null;
+
+ if (empty($mchPublicCertPath)) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_public_cert_path]');
+ }
+
+ $ssl = openssl_x509_parse(get_public_cert($mchPublicCertPath));
+
+ if (empty($ssl['serialNumberHex'])) {
+ throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Parse [mch_public_cert_path] Serial Number Error');
+ }
+
+ $auth = sprintf(
+ 'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
+ $config['mch_id'] ?? '',
+ $random,
+ $timestamp,
+ $ssl['serialNumberHex'],
+ get_wechat_sign($params, $contents),
+ );
+
+ return 'WECHATPAY2-SHA256-RSA2048 '.$auth;
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function v3GetContents(Rocket $rocket, int $timestamp, string $random): string
+ {
+ $request = $rocket->getRadar();
+
+ if (is_null($request)) {
+ throw new InvalidParamsException(Exception::REQUEST_NULL_ERROR);
+ }
+
+ $uri = $request->getUri();
+
+ return $request->getMethod()."\n".
+ $uri->getPath().(empty($uri->getQuery()) ? '' : '?'.$uri->getQuery())."\n".
+ $timestamp."\n".
+ $random."\n".
+ $this->v3PayloadToString($rocket->getPayload())."\n";
+ }
+
+ protected function v3PayloadToString(?Collection $payload): string
+ {
+ return (is_null($payload) || 0 === $payload->count()) ? '' : $this->jsonPacker->pack($payload->all());
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/CompleteComplaintPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/CompleteComplaintPlugin.php
new file mode 100644
index 0000000..ca15541
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/CompleteComplaintPlugin.php
@@ -0,0 +1,54 @@
+setDirection(OriginResponseDirection::class);
+
+ $payload = $rocket->getPayload();
+ $config = get_wechat_config($rocket->getParams());
+
+ $rocket->setPayload(new Collection([
+ 'complainted_mchid' => $payload->get('complainted_mchid', $config['mch_id'] ?? ''),
+ ]));
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('complaint_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant-service/complaints-v2/'.
+ $payload->get('complaint_id').
+ '/complete';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DeleteCallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DeleteCallbackPlugin.php
new file mode 100644
index 0000000..326854a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DeleteCallbackPlugin.php
@@ -0,0 +1,32 @@
+setDirection(OriginResponseDirection::class);
+
+ $rocket->setPayload(null);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/merchant-service/complaint-notifications';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DownloadMediaPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DownloadMediaPlugin.php
new file mode 100644
index 0000000..b5e8ce3
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/DownloadMediaPlugin.php
@@ -0,0 +1,43 @@
+getPayload();
+
+ if (!$payload->has('media_url')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return $payload->get('media_url');
+ }
+
+ protected function getMethod(): string
+ {
+ return 'GET';
+ }
+
+ protected function doSomething(Rocket $rocket): void
+ {
+ $rocket->setDirection(OriginResponseDirection::class);
+
+ $rocket->setPayload(null);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryCallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryCallbackPlugin.php
new file mode 100644
index 0000000..30857d6
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryCallbackPlugin.php
@@ -0,0 +1,29 @@
+setPayload(null);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/merchant-service/complaint-notifications';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintDetailPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintDetailPlugin.php
new file mode 100644
index 0000000..4c4fc49
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintDetailPlugin.php
@@ -0,0 +1,40 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('complaint_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant-service/complaints-v2/'.$payload->get('complaint_id');
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintNegotiationPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintNegotiationPlugin.php
new file mode 100644
index 0000000..7aef299
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintNegotiationPlugin.php
@@ -0,0 +1,45 @@
+setPayload(null);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+ $complaintId = $payload->get('complaint_id');
+
+ if (is_null($complaintId)) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ $payload->forget('complaint_id');
+
+ return 'v3/merchant-service/complaints-v2/'.
+ $complaintId.
+ '/negotiation-historys?'.$payload->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintsPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintsPlugin.php
new file mode 100644
index 0000000..aadc6a1
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/QueryComplaintsPlugin.php
@@ -0,0 +1,29 @@
+setPayload(null);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/merchant-service/complaints-v2?'.$rocket->getPayload()->query();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/ResponseComplaintPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/ResponseComplaintPlugin.php
new file mode 100644
index 0000000..b941e2b
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/ResponseComplaintPlugin.php
@@ -0,0 +1,57 @@
+setDirection(OriginResponseDirection::class);
+
+ $config = get_wechat_config($rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ $payload->forget('complaint_id');
+
+ if (!$payload->has('complainted_mchid')) {
+ $rocket->mergePayload([
+ 'complainted_mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('complaint_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant-service/complaints-v2/'.
+ $payload->get('complaint_id').
+ '/response';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/SetCallbackPlugin.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/SetCallbackPlugin.php
new file mode 100644
index 0000000..6bd83b5
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Risk/Complaints/SetCallbackPlugin.php
@@ -0,0 +1,23 @@
+getPayload()->forget('complaint_id');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getUri(Rocket $rocket): string
+ {
+ $payload = $rocket->getPayload();
+
+ if (!$payload->has('complaint_id')) {
+ throw new InvalidParamsException(Exception::MISSING_NECESSARY_PARAMS);
+ }
+
+ return 'v3/merchant-service/complaints-v2/'.
+ $payload->get('complaint_id').
+ '/update-refund-progress';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/AppShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/AppShortcut.php
new file mode 100644
index 0000000..c0d50e5
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/AppShortcut.php
@@ -0,0 +1,20 @@
+combinePlugins();
+ }
+
+ $typeMethod = Str::camel($params['_action'] ?? 'default').'Plugins';
+
+ if (method_exists($this, $typeMethod)) {
+ return $this->{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Query action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ ClosePlugin::class,
+ ];
+ }
+
+ protected function combinePlugins(): array
+ {
+ return [
+ \Yansongda\Pay\Plugin\Wechat\Pay\Combine\ClosePlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/MiniShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/MiniShortcut.php
new file mode 100644
index 0000000..3e2f4ce
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/MiniShortcut.php
@@ -0,0 +1,20 @@
+{$typeMethod}($params);
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Papay action [{$typeMethod}] not supported");
+ }
+
+ /**
+ * 返回只签约(委托代扣)参数.
+ *
+ * @see https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_3.shtml
+ */
+ public function ContractPlugins(): array
+ {
+ return [
+ PreparePlugin::class,
+ OnlyContractPlugin::class,
+ ];
+ }
+
+ /**
+ * 申请代扣.
+ *
+ * @see https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_8.shtml
+ */
+ public function applyPlugins(): array
+ {
+ return [
+ PreparePlugin::class,
+ ApplyPlugin::class,
+ RadarSignPlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ /**
+ * 支付中签约.
+ *
+ * @see https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_5.shtml
+ */
+ protected function defaultPlugins(array $params): array
+ {
+ return [
+ PreparePlugin::class,
+ ContractOrderPlugin::class,
+ RadarSignPlugin::class,
+ $this->getInvoke($params),
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function getInvoke(array $params): string
+ {
+ switch ($params['_type'] ?? 'default') {
+ case 'app':
+ return \Yansongda\Pay\Plugin\Wechat\Pay\App\InvokePrepayV2Plugin::class;
+
+ case 'mini':
+ return \Yansongda\Pay\Plugin\Wechat\Pay\Mini\InvokePrepayV2Plugin::class;
+
+ default:
+ return InvokePrepayV2Plugin::class;
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/PosShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/PosShortcut.php
new file mode 100644
index 0000000..b384de4
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/PosShortcut.php
@@ -0,0 +1,18 @@
+combinePlugins();
+ }
+
+ $typeMethod = Str::camel($params['_action'] ?? 'default').'Plugins';
+
+ if (method_exists($this, $typeMethod)) {
+ return $this->{$typeMethod}();
+ }
+
+ throw new InvalidParamsException(Exception::SHORTCUT_MULTI_ACTION_ERROR, "Query action [{$typeMethod}] not supported");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ QueryPlugin::class,
+ ];
+ }
+
+ protected function refundPlugins(): array
+ {
+ return [
+ QueryRefundPlugin::class,
+ ];
+ }
+
+ protected function combinePlugins(): array
+ {
+ return [
+ \Yansongda\Pay\Plugin\Wechat\Pay\Combine\QueryPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/RefundShortcut.php b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/RefundShortcut.php
new file mode 100644
index 0000000..5eacb01
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Plugin/Wechat/Shortcut/RefundShortcut.php
@@ -0,0 +1,18 @@
+setPayload(null);
+ }
+
+ protected function getUri(Rocket $rocket): string
+ {
+ return 'v3/certificates';
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Provider/AbstractProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Provider/AbstractProvider.php
new file mode 100644
index 0000000..58fc4a6
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Provider/AbstractProvider.php
@@ -0,0 +1,159 @@
+getPlugins($params);
+
+ if (empty($params['_no_common_plugins'])) {
+ $plugins = $this->mergeCommonPlugins($plugins);
+ }
+
+ return $this->pay($plugins, $params);
+ }
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params)
+ {
+ Logger::info('[AbstractProvider] 即将进行 pay 操作', func_get_args());
+
+ Event::dispatch(new Event\PayStarted($plugins, $params, null));
+
+ $this->verifyPlugin($plugins);
+
+ /* @var Pipeline $pipeline */
+ $pipeline = Pay::make(Pipeline::class);
+
+ /* @var Rocket $rocket */
+ $rocket = $pipeline
+ ->send((new Rocket())->setParams($params)->setPayload(new Collection()))
+ ->through($plugins)
+ ->via('assembly')
+ ->then(fn ($rocket) => $this->ignite($rocket))
+ ;
+
+ Event::dispatch(new Event\PayFinish($rocket));
+
+ $destination = $rocket->getDestination();
+
+ if (ArrayDirection::class === $rocket->getDirection() && $destination instanceof Collection) {
+ return $destination->toArray();
+ }
+
+ return $destination;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidResponseException
+ * @throws InvalidConfigException
+ */
+ public function ignite(Rocket $rocket): Rocket
+ {
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ /* @var HttpClientInterface $http */
+ $http = Pay::get(HttpClientInterface::class);
+
+ if (!$http instanceof ClientInterface) {
+ throw new InvalidConfigException(Exception::HTTP_CLIENT_CONFIG_ERROR);
+ }
+
+ Logger::info('[AbstractProvider] 准备请求支付服务商 API', $rocket->toArray());
+
+ Event::dispatch(new Event\ApiRequesting($rocket));
+
+ try {
+ $response = $http->sendRequest($rocket->getRadar());
+
+ $contents = (string) $response->getBody();
+
+ $rocket->setDestination($response->withBody(Utils::streamFor($contents)))
+ ->setDestinationOrigin($response->withBody(Utils::streamFor($contents)))
+ ;
+ } catch (Throwable $e) {
+ Logger::error('[AbstractProvider] 请求支付服务商 API 出错', ['message' => $e->getMessage(), 'rocket' => $rocket->toArray(), 'trace' => $e->getTrace()]);
+
+ throw new InvalidResponseException(Exception::REQUEST_RESPONSE_ERROR, $e->getMessage(), [], $e);
+ }
+
+ Logger::info('[AbstractProvider] 请求支付服务商 API 成功', ['response' => $response, 'rocket' => $rocket->toArray()]);
+
+ Event::dispatch(new Event\ApiRequested($rocket));
+
+ return $rocket;
+ }
+
+ abstract public function mergeCommonPlugins(array $plugins): array;
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function verifyPlugin(array $plugins): void
+ {
+ foreach ($plugins as $plugin) {
+ if (is_callable($plugin)) {
+ continue;
+ }
+
+ if ((is_object($plugin)
+ || (is_string($plugin) && class_exists($plugin)))
+ && in_array(PluginInterface::class, class_implements($plugin))) {
+ continue;
+ }
+
+ throw new InvalidParamsException(Exception::PLUGIN_ERROR, "[{$plugin}] is not incompatible");
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Provider/Alipay.php b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Alipay.php
new file mode 100644
index 0000000..4520ae7
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Alipay.php
@@ -0,0 +1,178 @@
+ 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ Pay::MODE_SANDBOX => 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8',
+ Pay::MODE_SERVICE => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ ];
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params)
+ {
+ $plugin = '\\Yansongda\\Pay\\Plugin\\Alipay\\Shortcut\\'.
+ Str::studly($shortcut).'Shortcut';
+
+ return $this->call($plugin, ...$params);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function find($order)
+ {
+ $order = is_array($order) ? $order : ['out_trade_no' => $order];
+
+ Event::dispatch(new Event\MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function cancel($order)
+ {
+ $order = is_array($order) ? $order : ['out_trade_no' => $order];
+
+ Event::dispatch(new Event\MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('cancel', [$order]);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function close($order)
+ {
+ $order = is_array($order) ? $order : ['out_trade_no' => $order];
+
+ Event::dispatch(new Event\MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('close', [$order]);
+ }
+
+ /**
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order)
+ {
+ Event::dispatch(new Event\MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback($contents = null, ?array $params = null): Collection
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new Event\CallbackReceived('alipay', $request->all(), $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ $request->merge($params)->all()
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(200, [], 'success');
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [PreparePlugin::class],
+ $plugins,
+ [RadarSignPlugin::class],
+ [LaunchPlugin::class, ParserPlugin::class],
+ );
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ */
+ protected function getCallbackParams($contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return Collection::wrap('GET' === $contents->getMethod() ? $contents->getQueryParams() :
+ $contents->getParsedBody());
+ }
+
+ $request = ServerRequest::fromGlobals();
+
+ return Collection::wrap(
+ array_merge($request->getQueryParams(), $request->getParsedBody())
+ );
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Provider/Unipay.php b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Unipay.php
new file mode 100644
index 0000000..8dedd91
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Unipay.php
@@ -0,0 +1,168 @@
+ 'https://gateway.95516.com/',
+ Pay::MODE_SANDBOX => 'https://gateway.test.95516.com/',
+ Pay::MODE_SERVICE => 'https://gateway.95516.com',
+ ];
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params)
+ {
+ $plugin = '\\Yansongda\\Pay\\Plugin\\Unipay\\Shortcut\\'.
+ Str::studly($shortcut).'Shortcut';
+
+ return $this->call($plugin, ...$params);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function find($order)
+ {
+ if (!is_array($order)) {
+ throw new InvalidParamsException(Exception::UNIPAY_FIND_STRING_NOT_SUPPORTED);
+ }
+
+ Event::dispatch(new Event\MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function cancel($order)
+ {
+ if (!is_array($order)) {
+ throw new InvalidParamsException(Exception::UNIPAY_CANCEL_STRING_NOT_SUPPORTED);
+ }
+
+ Event::dispatch(new Event\MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('cancel', [$order]);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws InvalidParamsException
+ */
+ public function close($order)
+ {
+ throw new InvalidParamsException(Exception::METHOD_NOT_SUPPORTED, 'Unipay does not support close api');
+ }
+
+ /**
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order)
+ {
+ Event::dispatch(new Event\MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback($contents = null, ?array $params = null): Collection
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new Event\CallbackReceived('unipay', $request->all(), $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ $request->merge($params)->all()
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(200, [], 'success');
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [PreparePlugin::class],
+ $plugins,
+ [RadarSignPlugin::class],
+ [LaunchPlugin::class, ParserPlugin::class],
+ );
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ */
+ protected function getCallbackParams($contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return Collection::wrap($contents->getParsedBody());
+ }
+
+ $request = ServerRequest::fromGlobals();
+
+ return Collection::wrap($request->getParsedBody());
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Provider/Wechat.php b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Wechat.php
new file mode 100644
index 0000000..ece1ed9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Provider/Wechat.php
@@ -0,0 +1,178 @@
+ 'https://api.mch.weixin.qq.com/',
+ Pay::MODE_SANDBOX => 'https://api.mch.weixin.qq.com/sandboxnew/',
+ Pay::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
+ ];
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params)
+ {
+ $plugin = '\\Yansongda\\Pay\\Plugin\\Wechat\\Shortcut\\'.
+ Str::studly($shortcut).'Shortcut';
+
+ return $this->call($plugin, ...$params);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function find($order)
+ {
+ $order = is_array($order) ? $order : ['transaction_id' => $order];
+
+ Event::dispatch(new Event\MethodCalled('wechat', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @throws InvalidParamsException
+ */
+ public function cancel($order): void
+ {
+ throw new InvalidParamsException(Exception::METHOD_NOT_SUPPORTED, 'Wechat does not support cancel api');
+ }
+
+ /**
+ * @param array|string $order
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function close($order): void
+ {
+ $order = is_array($order) ? $order : ['out_trade_no' => $order];
+
+ Event::dispatch(new Event\MethodCalled('wechat', __METHOD__, $order, null));
+
+ $this->__call('close', [$order]);
+ }
+
+ /**
+ * @return array|Collection
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order)
+ {
+ Event::dispatch(new Event\MethodCalled('wechat', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback($contents = null, ?array $params = null): Collection
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new Event\CallbackReceived('wechat', clone $request, $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ ['request' => $request, 'params' => $params]
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(
+ 200,
+ ['Content-Type' => 'application/json'],
+ json_encode(['code' => 'SUCCESS', 'message' => '成功']),
+ );
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [PreparePlugin::class],
+ $plugins,
+ [RadarSignPlugin::class],
+ [LaunchPlugin::class, ParserPlugin::class],
+ );
+ }
+
+ /**
+ * @param null|array|ServerRequestInterface $contents
+ */
+ protected function getCallbackParams($contents = null): ServerRequestInterface
+ {
+ if (is_array($contents) && isset($contents['body'], $contents['headers'])) {
+ return new ServerRequest('POST', 'http://localhost', $contents['headers'], $contents['body']);
+ }
+
+ if (is_array($contents)) {
+ return new ServerRequest('POST', 'http://localhost', [], json_encode($contents));
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return $contents;
+ }
+
+ return ServerRequest::fromGlobals();
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Request.php b/server/addons/epay/library/v3/Yansongda/Pay/Request.php
new file mode 100644
index 0000000..6628591
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Request.php
@@ -0,0 +1,27 @@
+ $this->getUri()->__toString(),
+ 'method' => $this->getMethod(),
+ 'headers' => $this->getHeaders(),
+ 'body' => (string) $this->getBody(),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Rocket.php b/server/addons/epay/library/v3/Yansongda/Pay/Rocket.php
new file mode 100644
index 0000000..ae99249
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Rocket.php
@@ -0,0 +1,148 @@
+radar;
+ }
+
+ public function setRadar(?RequestInterface $radar): Rocket
+ {
+ $this->radar = $radar;
+
+ return $this;
+ }
+
+ public function getParams(): array
+ {
+ return $this->params;
+ }
+
+ public function setParams(array $params): Rocket
+ {
+ $this->params = $params;
+
+ return $this;
+ }
+
+ public function mergeParams(array $params): Rocket
+ {
+ $this->params = array_merge($this->params, $params);
+
+ return $this;
+ }
+
+ public function getPayload(): ?Collection
+ {
+ return $this->payload;
+ }
+
+ public function setPayload(?Collection $payload): Rocket
+ {
+ $this->payload = $payload;
+
+ return $this;
+ }
+
+ public function mergePayload(array $payload): Rocket
+ {
+ if (empty($this->payload)) {
+ $this->payload = new Collection();
+ }
+
+ $this->payload = $this->payload->merge($payload);
+
+ return $this;
+ }
+
+ public function getPacker(): string
+ {
+ return $this->packer;
+ }
+
+ public function setPacker(string $packer): Rocket
+ {
+ $this->packer = $packer;
+
+ return $this;
+ }
+
+ public function getDirection(): string
+ {
+ return $this->direction;
+ }
+
+ public function setDirection(string $direction): Rocket
+ {
+ $this->direction = $direction;
+
+ return $this;
+ }
+
+ /**
+ * @return null|array|Collection|MessageInterface
+ */
+ public function getDestination()
+ {
+ return $this->destination;
+ }
+
+ /**
+ * @param null|array|Collection|MessageInterface $destination
+ */
+ public function setDestination($destination): Rocket
+ {
+ $this->destination = $destination;
+
+ return $this;
+ }
+
+ public function getDestinationOrigin(): ?MessageInterface
+ {
+ return $this->destinationOrigin;
+ }
+
+ public function setDestinationOrigin(?MessageInterface $destinationOrigin): Rocket
+ {
+ $this->destinationOrigin = $destinationOrigin;
+
+ return $this;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Service/AlipayServiceProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Service/AlipayServiceProvider.php
new file mode 100644
index 0000000..9e0492a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Service/AlipayServiceProvider.php
@@ -0,0 +1,26 @@
+ [
+ 'enable' => false,
+ 'file' => null,
+ 'identify' => 'yansongda.pay',
+ 'level' => 'debug',
+ 'type' => 'daily',
+ 'max_files' => 30,
+ ],
+ 'http' => [
+ 'timeout' => 5.0,
+ 'connect_timeout' => 3.0,
+ 'headers' => [
+ 'User-Agent' => 'yansongda/pay-v3',
+ ],
+ ],
+ 'mode' => Pay::MODE_NORMAL,
+ ];
+
+ /**
+ * @param mixed $data
+ *
+ * @throws ContainerException
+ */
+ public function register($data = null): void
+ {
+ $config = new Config(array_replace_recursive($this->config, $data ?? []));
+
+ Pay::set(ConfigInterface::class, $config);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Service/ContainerServiceProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Service/ContainerServiceProvider.php
new file mode 100644
index 0000000..649ea2d
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Service/ContainerServiceProvider.php
@@ -0,0 +1,107 @@
+ LaravelContainer::class,
+ 'hyperf' => HyperfContainer::class,
+ ];
+
+ /**
+ * @param mixed $data
+ *
+ * @throws ContainerException
+ */
+ public function register($data = null): void
+ {
+ if ($data instanceof ContainerInterface || $data instanceof Closure) {
+ Pay::setContainer($data);
+
+ return;
+ }
+
+ if (Pay::hasContainer()) {
+ return;
+ }
+
+ foreach ($this->detectApplication as $framework => $application) {
+ $method = $framework.'Application';
+
+ if (class_exists($application) && method_exists($this, $method) && $this->{$method}()) {
+ return;
+ }
+ }
+
+ $this->defaultApplication();
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ContainerNotFoundException
+ */
+ protected function laravelApplication(): bool
+ {
+ Pay::setContainer(static fn () => LaravelContainer::getInstance());
+
+ Pay::set(\Yansongda\Pay\Contract\ContainerInterface::class, LaravelContainer::getInstance());
+
+ if (!Pay::has(ContainerInterface::class)) {
+ Pay::set(ContainerInterface::class, LaravelContainer::getInstance());
+ }
+
+ return true;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ContainerNotFoundException
+ */
+ protected function hyperfApplication(): bool
+ {
+ if (!HyperfContainer::hasContainer()) {
+ return false;
+ }
+
+ Pay::setContainer(static fn () => HyperfContainer::getContainer());
+
+ Pay::set(\Yansongda\Pay\Contract\ContainerInterface::class, HyperfContainer::getContainer());
+
+ if (!Pay::has(ContainerInterface::class)) {
+ Pay::set(ContainerInterface::class, HyperfContainer::getContainer());
+ }
+
+ return true;
+ }
+
+ /**
+ * @throws ContainerNotFoundException
+ */
+ protected function defaultApplication(): void
+ {
+ if (!class_exists(DefaultContainer::class)) {
+ throw new ContainerNotFoundException('Init failed! Maybe you should install `hyperf/pimple` first', Exception::CONTAINER_NOT_FOUND);
+ }
+
+ $container = (new DefaultContainer())();
+
+ Pay::setContainer($container);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Service/EventServiceProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Service/EventServiceProvider.php
new file mode 100644
index 0000000..ffa1c24
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Service/EventServiceProvider.php
@@ -0,0 +1,26 @@
+get('http', []));
+
+ Pay::set(HttpClientInterface::class, $service);
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Service/LoggerServiceProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Service/LoggerServiceProvider.php
new file mode 100644
index 0000000..d38632b
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Service/LoggerServiceProvider.php
@@ -0,0 +1,37 @@
+get('logger.enable', false)) {
+ $logger = new Logger(array_merge(
+ ['identify' => 'yansongda.pay'],
+ $config->get('logger', [])
+ ));
+
+ Pay::set(LoggerInterface::class, $logger);
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Service/UnipayServiceProvider.php b/server/addons/epay/library/v3/Yansongda/Pay/Service/UnipayServiceProvider.php
new file mode 100644
index 0000000..6ee2691
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Service/UnipayServiceProvider.php
@@ -0,0 +1,26 @@
+getCerts($config);
+ $ssl = openssl_x509_parse($certs['cert'] ?? '');
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::UNIPAY_CONFIG_ERROR, 'Parse `mch_cert_path` Error');
+ }
+
+ $certs['cert_id'] = $ssl['serialNumber'] ?? '';
+
+ Pay::get(ConfigInterface::class)->set('unipay.'.$tenant.'.certs', $certs);
+
+ return $certs['cert_id'];
+ }
+
+ /**
+ * @return array ['cert' => 公钥, 'pkey' => 私钥, 'extracerts' => array]
+ *
+ * @throws InvalidConfigException
+ */
+ protected function getCerts(array $config): array
+ {
+ $path = $config['mch_cert_path'] ?? null;
+ $password = $config['mch_cert_password'] ?? null;
+
+ if (is_null($path) || is_null($password)) {
+ throw new InvalidConfigException(Exception::UNIPAY_CONFIG_ERROR, 'Missing Unipay Config -- [mch_cert_path] or [mch_cert_password]');
+ }
+
+ if (false === openssl_pkcs12_read(file_get_contents($path), $certs, $password)) {
+ throw new InvalidConfigException(Exception::UNIPAY_CONFIG_ERROR, 'Read `mch_cert_path` Error');
+ }
+
+ return $certs;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Pay/Traits/HasWechatEncryption.php b/server/addons/epay/library/v3/Yansongda/Pay/Traits/HasWechatEncryption.php
new file mode 100644
index 0000000..c9adb60
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Pay/Traits/HasWechatEncryption.php
@@ -0,0 +1,61 @@
+getParams();
+ $config = get_alipay_config($params);
+ $serviceProviderId = $config['service_provider_id'] ?? null;
+
+ if (Pay::MODE_SERVICE !== ($config['mode'] ?? Pay::MODE_NORMAL)
+ || empty($serviceProviderId)) {
+ return;
+ }
+
+ $rocket->mergeParams([
+ 'extend_params' => array_merge($params['extend_params'] ?? [], ['sys_service_provider_id' => $serviceProviderId]),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Arr.php b/server/addons/epay/library/v3/Yansongda/Supports/Arr.php
new file mode 100644
index 0000000..a888aca
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Arr.php
@@ -0,0 +1,624 @@
+all();
+ } elseif (!is_array($values)) {
+ continue;
+ }
+ $results[] = $values;
+ }
+
+ return array_merge([], ...$results);
+ }
+
+ /**
+ * Cross join the given arrays, returning all possible permutations.
+ *
+ * @param array ...$arrays
+ */
+ public static function crossJoin(...$arrays): array
+ {
+ $results = [[]];
+ foreach ($arrays as $index => $array) {
+ $append = [];
+ foreach ($results as $product) {
+ foreach ($array as $item) {
+ $product[$index] = $item;
+ $append[] = $product;
+ }
+ }
+ $results = $append;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Divide an array into two arrays. One with keys and the other with values.
+ */
+ public static function divide(array $array): array
+ {
+ return [array_keys($array), array_values($array)];
+ }
+
+ /**
+ * Flatten a multi-dimensional associative array with dots.
+ */
+ public static function dot(array $array, string $prepend = ''): array
+ {
+ $results = [];
+ foreach ($array as $key => $value) {
+ if (is_array($value) && !empty($value)) {
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+ } else {
+ $results[$prepend.$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Get all of the given array except for a specified array of keys.
+ *
+ * @param array|string $keys
+ */
+ public static function except(array $array, $keys): array
+ {
+ static::forget($array, $keys);
+
+ return $array;
+ }
+
+ /**
+ * Determine if the given key exists in the provided array.
+ *
+ * @param array|\ArrayAccess $array
+ * @param int|string $key
+ */
+ public static function exists($array, $key): bool
+ {
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Return the first element in an array passing a given truth test.
+ *
+ * @param mixed|null $default
+ */
+ public static function first(array $array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ if (empty($array)) {
+ return $default;
+ }
+ foreach ($array as $item) {
+ return $item;
+ }
+ }
+ foreach ($array as $key => $value) {
+ if (call_user_func($callback, $value, $key)) {
+ return $value;
+ }
+ }
+
+ return $default;
+ }
+
+ /**
+ * Return the last element in an array passing a given truth test.
+ *
+ * @param mixed|null $default
+ */
+ public static function last(array $array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ return empty($array) ? $default : end($array);
+ }
+
+ return static::first(array_reverse($array, true), $callback, $default);
+ }
+
+ /**
+ * Flatten a multi-dimensional array into a single level.
+ *
+ * @param float|int $depth
+ */
+ public static function flatten(array $array, $depth = INF): array
+ {
+ $result = [];
+ foreach ($array as $item) {
+ $item = $item instanceof Collection ? $item->all() : $item;
+ if (!is_array($item)) {
+ $result[] = $item;
+ } elseif (1 === $depth) {
+ $result = array_merge($result, array_values($item));
+ } else {
+ $result = array_merge($result, static::flatten($item, $depth - 1));
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove one or many array items from a given array using "dot" notation.
+ *
+ * @param array|string $keys
+ */
+ public static function forget(array &$array, $keys): void
+ {
+ $original = &$array;
+ $keys = (array) $keys;
+ if (0 === count($keys)) {
+ return;
+ }
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+ continue;
+ }
+ $parts = explode('.', $key);
+ // clean up before each pass
+ $array = &$original;
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+ if (isset($array[$part]) && is_array($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ /**
+ * Get an item from an array using "dot" notation.
+ *
+ * @param array|\ArrayAccess $array
+ * @param int|string|null $key
+ * @param mixed $default
+ */
+ public static function get($array, $key = null, $default = null)
+ {
+ if (!static::accessible($array)) {
+ return $default;
+ }
+ if (is_null($key)) {
+ return $array;
+ }
+ if (static::exists($array, $key)) {
+ return $array[$key];
+ }
+ if (!is_string($key) || false === strpos($key, '.')) {
+ return $array[$key] ?? $default;
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($array) && static::exists($array, $segment)) {
+ $array = $array[$segment];
+ } else {
+ return $default;
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Check if an item or items exist in an array using "dot" notation.
+ *
+ * @param array|\ArrayAccess $array
+ * @param array|string|null $keys
+ */
+ public static function has($array, $keys): bool
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+ $keys = (array) $keys;
+ if (!$array) {
+ return false;
+ }
+ if ([] === $keys) {
+ return false;
+ }
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+ if (static::exists($array, $key)) {
+ continue;
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines if an array is associative.
+ * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+ */
+ public static function isAssoc(array $array): bool
+ {
+ $keys = array_keys($array);
+
+ return array_keys($keys) !== $keys;
+ }
+
+ /**
+ * Get a subset of the items from the given array.
+ *
+ * @param array|string $keys
+ */
+ public static function only(array $array, $keys): array
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * Pluck an array of values from an array.
+ *
+ * @param array|string $value
+ * @param array|string|null $key
+ */
+ public static function pluck(array $array, $value, $key = null): array
+ {
+ $results = [];
+
+ foreach ($array as $item) {
+ $itemValue = data_get($item, $value);
+ // If the key is "null", we will just append the value to the array and keep
+ // looping. Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+ $results[$itemKey] = $itemValue;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Push an item onto the beginning of an array.
+ *
+ * @param mixed|null $key
+ * @param mixed $value
+ */
+ public static function prepend(array $array, $value, $key = null): array
+ {
+ if (is_null($key)) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Get a value from the array, and remove it.
+ *
+ * @param mixed|null $default
+ */
+ public static function pull(array &$array, string $key, $default = null)
+ {
+ $value = static::get($array, $key, $default);
+ static::forget($array, $key);
+
+ return $value;
+ }
+
+ /**
+ * Get one or a specified number of random values from an array.
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function random(array $array, int $number = 1): array
+ {
+ $count = count($array);
+ if ($number > $count) {
+ throw new InvalidArgumentException("You requested $number items, but there are only $count items available.");
+ }
+
+ if (0 === $number) {
+ return [];
+ }
+
+ $keys = array_rand($array, $number);
+ $results = [];
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ * If no key is given to the method, the entire array will be replaced.
+ *
+ * @param int|string|null $key
+ * @param mixed $value
+ */
+ public static function set(array &$array, $key, $value): array
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+ if (!is_string($key)) {
+ $array[$key] = $value;
+
+ return $array;
+ }
+ $keys = explode('.', $key);
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (!isset($array[$key]) || !is_array($array[$key])) {
+ $array[$key] = [];
+ }
+ $array = &$array[$key];
+ }
+ $array[array_shift($keys)] = $value;
+
+ return $array;
+ }
+
+ /**
+ * Shuffle the given array and return the result.
+ */
+ public static function shuffle(array $array, int $seed = null): array
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ srand($seed);
+ usort($array, function () {
+ return rand(-1, 1);
+ });
+ }
+
+ return $array;
+ }
+
+ /**
+ * Sort the array using the given Closure.
+ */
+ public static function sort(array $array, callable $callback): array
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ $results[$key] = $callback($value);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Recursively sort an array by keys and values.
+ */
+ public static function sortRecursive(array $array): array
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = static::sortRecursive($value);
+ }
+ }
+ if (static::isAssoc($array)) {
+ ksort($array);
+ } else {
+ sort($array);
+ }
+
+ return $array;
+ }
+
+ public static function query(array $array, int $encodingType = PHP_QUERY_RFC1738): string
+ {
+ return http_build_query($array, '', '&', $encodingType);
+ }
+
+ public static function toString(array $array, string $separator = '&'): string
+ {
+ $result = '';
+
+ foreach ($array as $key => $value) {
+ $result .= $key.'='.$value.$separator;
+ }
+
+ return substr($result, 0, 0 - Str::length($separator));
+ }
+
+ /**
+ * Filter the array using the given callback.
+ */
+ public static function where(array $array, callable $callback): array
+ {
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+ }
+
+ /**
+ * If the given value is not an array and not null, wrap it in one.
+ *
+ * @param mixed $value
+ */
+ public static function wrap($value): array
+ {
+ if (is_null($value)) {
+ return [];
+ }
+
+ return !is_array($value) ? [$value] : $value;
+ }
+
+ /**
+ * Make array elements unique.
+ */
+ public static function unique(array $array): array
+ {
+ $result = [];
+ foreach ($array as $key => $item) {
+ if (is_array($item)) {
+ $result[$key] = self::unique($item);
+ } else {
+ $result[$key] = $item;
+ }
+ }
+
+ if (!self::isAssoc($result)) {
+ return array_unique($result);
+ }
+
+ return $result;
+ }
+
+ public static function merge(array $array1, array $array2, bool $unique = true): array
+ {
+ $isAssoc = static::isAssoc($array1 ?: $array2);
+ if ($isAssoc) {
+ foreach ($array2 as $key => $value) {
+ if (is_array($value)) {
+ $array1[$key] = static::merge($array1[$key] ?? [], $value, $unique);
+ } else {
+ $array1[$key] = $value;
+ }
+ }
+ } else {
+ foreach ($array2 as $value) {
+ if ($unique && in_array($value, $array1, true)) {
+ continue;
+ }
+ $array1[] = $value;
+ }
+
+ $array1 = array_values($array1);
+ }
+
+ return $array1;
+ }
+
+ /**
+ * Convert encoding.
+ */
+ public static function encoding(array $array, string $to_encoding, string $from_encoding = 'gb2312'): array
+ {
+ $encoded = [];
+
+ foreach ($array as $key => $value) {
+ $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) :
+ mb_convert_encoding($value, $to_encoding, $from_encoding);
+ }
+
+ return $encoded;
+ }
+
+ /**
+ * camelCaseKey.
+ *
+ * @param mixed $data
+ *
+ * @return mixed
+ */
+ public static function camelCaseKey($data)
+ {
+ if (!self::accessible($data) &&
+ !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $result = [];
+
+ foreach ((is_object($data) ? $data->toArray() : $data) as $key => $value) {
+ $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * snakeCaseKey.
+ *
+ * @param mixed $data
+ *
+ * @return mixed
+ */
+ public static function snakeCaseKey($data)
+ {
+ if (!self::accessible($data) &&
+ !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $result = [];
+
+ foreach ((is_object($data) ? $data->toArray() : $data) as $key => $value) {
+ $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value);
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Collection.php b/server/addons/epay/library/v3/Yansongda/Supports/Collection.php
new file mode 100644
index 0000000..292d278
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Collection.php
@@ -0,0 +1,531 @@
+getArrayableItems($items) as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * Wrap the given value in a collection if applicable.
+ *
+ * @param mixed $value
+ */
+ public static function wrap($value): self
+ {
+ return $value instanceof self ? new static($value) : new static(Arr::wrap($value));
+ }
+
+ /**
+ * Get the underlying items from the given collection if applicable.
+ *
+ * @param array|static $value
+ */
+ public static function unwrap($value): array
+ {
+ return $value instanceof self ? $value->all() : $value;
+ }
+
+ /**
+ * Return all items.
+ */
+ public function all(): array
+ {
+ return $this->items;
+ }
+
+ /**
+ * Return specific items.
+ */
+ public function only(array $keys): array
+ {
+ $return = [];
+
+ foreach ($keys as $key) {
+ $value = $this->get($key);
+
+ if (!is_null($value)) {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param mixed $keys
+ */
+ public function except($keys): self
+ {
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ /**
+ * Run a filter over each of the items.
+ */
+ public function filter(callable $callback = null): self
+ {
+ if ($callback) {
+ return new static(Arr::where($this->items, $callback));
+ }
+
+ return new static(array_filter($this->items));
+ }
+
+ /**
+ * Merge the collection with the given items.
+ *
+ * @param mixed $items
+ */
+ public function merge($items): self
+ {
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * To determine Whether the specified element exists.
+ *
+ * @param string|int $key
+ */
+ public function has($key): bool
+ {
+ return !is_null(Arr::get($this->items, $key));
+ }
+
+ /**
+ * Retrieve the first item.
+ *
+ * @return mixed
+ */
+ public function first()
+ {
+ return reset($this->items);
+ }
+
+ /**
+ * Retrieve the last item.
+ *
+ * @return mixed
+ */
+ public function last()
+ {
+ $end = end($this->items);
+
+ reset($this->items);
+
+ return $end;
+ }
+
+ /**
+ * add the item value.
+ *
+ * @param string|int|null $key
+ * @param mixed $value
+ */
+ public function add($key, $value): void
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ /**
+ * Set the item value.
+ *
+ * @param string|int|null $key
+ * @param mixed $value
+ */
+ public function set($key, $value): void
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ /**
+ * Retrieve item from Collection.
+ *
+ * @param string|int|null $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get($key = null, $default = null)
+ {
+ return Arr::get($this->items, $key, $default);
+ }
+
+ /**
+ * Remove item form Collection.
+ *
+ * @param string|int $key
+ */
+ public function forget($key): void
+ {
+ Arr::forget($this->items, $key);
+ }
+
+ /**
+ * Get a flattened array of the items in the collection.
+ *
+ * @param float|int $depth
+ */
+ public function flatten($depth = INF): self
+ {
+ return new static(Arr::flatten($this->items, $depth));
+ }
+
+ /**
+ * Run a map over each of the items.
+ */
+ public function map(callable $callback): self
+ {
+ $keys = array_keys($this->items);
+ $items = array_map($callback, $this->items, $keys);
+
+ return new static(array_combine($keys, $items));
+ }
+
+ /**
+ * Get and remove the last item from the collection.
+ *
+ * @return mixed
+ */
+ public function pop()
+ {
+ return array_pop($this->items);
+ }
+
+ /**
+ * Push an item onto the beginning of the collection.
+ *
+ * @param mixed|null $key
+ * @param mixed $value
+ */
+ public function prepend($value, $key = null): self
+ {
+ $this->items = Arr::prepend($this->items, $value, $key);
+
+ return $this;
+ }
+
+ /**
+ * Push an item onto the end of the collection.
+ *
+ * @param mixed $value
+ */
+ public function push($value): self
+ {
+ $this->offsetSet(null, $value);
+
+ return $this;
+ }
+
+ /**
+ * Get and remove an item from the collection.
+ *
+ * @param mixed $default
+ * @param mixed $key
+ *
+ * @return mixed
+ */
+ public function pull($key, $default = null)
+ {
+ return Arr::pull($this->items, $key, $default);
+ }
+
+ /**
+ * Put an item in the collection by key.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ public function put($key, $value): self
+ {
+ $this->offsetSet($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * Get one or a specified number of items randomly from the collection.
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function random(?int $number = null): self
+ {
+ return new static(Arr::random($this->items, $number ?? 1));
+ }
+
+ /**
+ * Reduce the collection to a single value.
+ *
+ * @param mixed|null $initial
+ */
+ public function reduce(callable $callback, $initial = null)
+ {
+ return array_reduce($this->items, $callback, $initial);
+ }
+
+ /**
+ * Reset the keys on the underlying array.
+ */
+ public function values(): self
+ {
+ return new static(array_values($this->items));
+ }
+
+ /**
+ * Determine if all items in the collection pass the given test.
+ *
+ * @param callable|string $key
+ */
+ public function every($key): bool
+ {
+ $callback = $this->valueRetriever($key);
+
+ foreach ($this->items as $k => $v) {
+ if (!$callback($v, $k)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Chunk the underlying collection array.
+ */
+ public function chunk(int $size): self
+ {
+ if ($size <= 0) {
+ return new static();
+ }
+ $chunks = [];
+ foreach (array_chunk($this->items, $size, true) as $chunk) {
+ $chunks[] = new static($chunk);
+ }
+
+ return new static($chunks);
+ }
+
+ /**
+ * Sort through each item with a callback.
+ */
+ public function sort(callable $callback = null): self
+ {
+ $items = $this->items;
+ $callback ? uasort($items, $callback) : asort($items);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection using the given callback.
+ *
+ * @param callable|string $callback
+ */
+ public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ $results = [];
+ $callback = $this->valueRetriever($callback);
+ // First we will loop through the items and get the comparator from a callback
+ // function which we were given. Then, we will sort the returned values and
+ // and grab the corresponding values for the sorted keys from this array.
+ foreach ($this->items as $key => $value) {
+ $results[$key] = $callback($value, $key);
+ }
+ $descending ? arsort($results, $options) : asort($results, $options);
+ // Once we have sorted all of the keys in the array, we will loop through them
+ // and grab the corresponding model so we can set the underlying items list
+ // to the sorted version. Then we'll just return the collection instance.
+ foreach (array_keys($results) as $key) {
+ $results[$key] = $this->items[$key];
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Sort the collection in descending order using the given callback.
+ *
+ * @param callable|string $callback
+ */
+ public function sortByDesc($callback, int $options = SORT_REGULAR): self
+ {
+ return $this->sortBy($callback, $options, true);
+ }
+
+ /**
+ * Sort the collection keys.
+ */
+ public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ $items = $this->items;
+ $descending ? krsort($items, $options) : ksort($items, $options);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection keys in descending order.
+ */
+ public function sortKeysDesc(int $options = SORT_REGULAR): self
+ {
+ return $this->sortKeys($options, true);
+ }
+
+ public function query(int $encodingType = PHP_QUERY_RFC1738): string
+ {
+ return Arr::query($this->all(), $encodingType);
+ }
+
+ public function toString(string $separator = '&'): string
+ {
+ return Arr::toString($this->all(), $separator);
+ }
+
+ /**
+ * Build to array.
+ */
+ public function toArray(): array
+ {
+ return $this->all();
+ }
+
+ /**
+ * Build to json.
+ */
+ public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+ {
+ return json_encode($this->all(), $option);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Retrieve an external iterator.
+ *
+ * @see http://php.net/manual/en/iteratoraggregate.getiterator.php
+ *
+ * @return ArrayIterator An instance of an object implementing Iterator or
+ * ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ /**
+ * (PHP 5 >= 5.1.0)
+ * Count elements of an object.
+ *
+ * @see http://php.net/manual/en/countable.count.php
+ *
+ * @return int The custom count as an integer.
+ *
+ *
+ * The return value is cast to an integer
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->items);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Offset to unset.
+ *
+ * @see http://php.net/manual/en/arrayaccess.offsetunset.php
+ *
+ * @param mixed $offset
+ * The offset to unset.
+ *
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset)
+ {
+ if ($this->offsetExists($offset)) {
+ $this->forget($offset);
+ }
+ }
+
+ /**
+ * Determine if the given value is callable, but not a string.
+ *
+ * @param mixed $value
+ */
+ protected function useAsCallable($value): bool
+ {
+ return !is_string($value) && is_callable($value);
+ }
+
+ /**
+ * Get a value retrieving callback.
+ *
+ * @param mixed $value
+ */
+ protected function valueRetriever($value): callable
+ {
+ if ($this->useAsCallable($value)) {
+ return $value;
+ }
+
+ return function ($item) use ($value) {
+ return data_get($item, $value);
+ };
+ }
+
+ /**
+ * Results array of items from Collection or Arrayable.
+ *
+ * @param mixed $items
+ */
+ protected function getArrayableItems($items): array
+ {
+ if (is_array($items)) {
+ return $items;
+ }
+
+ if ($items instanceof self) {
+ return $items->all();
+ }
+
+ if ($items instanceof JsonSerializable) {
+ return $items->jsonSerialize();
+ }
+
+ return (array) $items;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Config.php b/server/addons/epay/library/v3/Yansongda/Supports/Config.php
new file mode 100644
index 0000000..c6e3ae9
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Config.php
@@ -0,0 +1,9 @@
+all();
+ } elseif (!is_array($target)) {
+ return value($default);
+ }
+ $result = [];
+ foreach ($target as $item) {
+ $result[] = data_get($item, $key);
+ }
+
+ return in_array('*', $key) ? Arr::collapse($result) : $result;
+ }
+ if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+ $target = $target[$segment];
+ } elseif (is_object($target) && isset($target->{$segment})) {
+ $target = $target->{$segment};
+ } else {
+ return value($default);
+ }
+ }
+
+ return $target;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Logger.php b/server/addons/epay/library/v3/Yansongda/Supports/Logger.php
new file mode 100644
index 0000000..35ac7c3
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Logger.php
@@ -0,0 +1,174 @@
+ null,
+ 'identify' => 'yansongda.supports',
+ 'level' => BaseLogger::DEBUG,
+ 'type' => 'daily',
+ 'max_files' => 30,
+ ];
+
+ /**
+ * Bootstrap.
+ */
+ public function __construct(array $config = [])
+ {
+ $this->setConfig($config);
+ }
+
+ /**
+ * Forward call.
+ *
+ * @throws Exception
+ */
+ public function __call(string $method, array $args): void
+ {
+ call_user_func_array([$this->getLogger(), $method], $args);
+ }
+
+ /**
+ * Set logger.
+ */
+ public function setLogger(LoggerInterface $logger): Logger
+ {
+ $this->logger = $logger;
+
+ return $this;
+ }
+
+ /**
+ * Return the logger instance.
+ *
+ * @throws Exception
+ */
+ public function getLogger(): LoggerInterface
+ {
+ return $this->logger ??= $this->createLogger();
+ }
+
+ public function createLogger(): BaseLogger
+ {
+ $handler = $this->getHandler();
+
+ $handler->setFormatter($this->getFormatter());
+
+ $logger = new BaseLogger($this->config['identify']);
+
+ $logger->pushHandler($handler);
+
+ return $logger;
+ }
+
+ /**
+ * setFormatter.
+ *
+ * @return $this
+ */
+ public function setFormatter(FormatterInterface $formatter): self
+ {
+ $this->formatter = $formatter;
+
+ return $this;
+ }
+
+ /**
+ * getFormatter.
+ */
+ public function getFormatter(): FormatterInterface
+ {
+ return $this->formatter ??= $this->createFormatter();
+ }
+
+ /**
+ * createFormatter.
+ */
+ public function createFormatter(): LineFormatter
+ {
+ return new LineFormatter(
+ "%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n",
+ null,
+ false,
+ true
+ );
+ }
+
+ /**
+ * setHandler.
+ *
+ * @return $this
+ */
+ public function setHandler(AbstractProcessingHandler $handler): self
+ {
+ $this->handler = $handler;
+
+ return $this;
+ }
+
+ public function getHandler(): AbstractProcessingHandler
+ {
+ return $this->handler ??= $this->createHandler();
+ }
+
+ public function createHandler(): AbstractProcessingHandler
+ {
+ $file = $this->config['file'] ?? sys_get_temp_dir().'/logs/'.$this->config['identify'].'.log';
+
+ if ('single' === $this->config['type']) {
+ return new StreamHandler($file, $this->config['level']);
+ }
+
+ return new RotatingFileHandler($file, $this->config['max_files'], $this->config['level']);
+ }
+
+ /**
+ * setConfig.
+ *
+ * @return $this
+ */
+ public function setConfig(array $config): self
+ {
+ $this->config = array_merge($this->config, $config);
+
+ return $this;
+ }
+
+ /**
+ * getConfig.
+ */
+ public function getConfig(): array
+ {
+ return $this->config;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Logger/StdoutHandler.php b/server/addons/epay/library/v3/Yansongda/Supports/Logger/StdoutHandler.php
new file mode 100644
index 0000000..ef53188
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Logger/StdoutHandler.php
@@ -0,0 +1,33 @@
+output = $output ?? new ConsoleOutput();
+
+ parent::__construct($level, $bubble);
+ }
+
+ /**
+ * Writes the record down to the log of the implementing handler.
+ */
+ protected function write(array $record): void
+ {
+ $this->output->writeln($record['formatted']);
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Pipeline.php b/server/addons/epay/library/v3/Yansongda/Supports/Pipeline.php
new file mode 100644
index 0000000..8a6ea20
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Pipeline.php
@@ -0,0 +1,158 @@
+container = $container;
+ }
+
+ /**
+ * Set the object being sent through the pipeline.
+ *
+ * @param mixed $passable
+ */
+ public function send($passable): self
+ {
+ $this->passable = $passable;
+
+ return $this;
+ }
+
+ /**
+ * Set the array of pipes.
+ *
+ * @param array|mixed $pipes
+ */
+ public function through($pipes): self
+ {
+ $this->pipes = is_array($pipes) ? $pipes : func_get_args();
+
+ return $this;
+ }
+
+ /**
+ * Set the method to call on the pipes.
+ */
+ public function via(string $method): self
+ {
+ $this->method = $method;
+
+ return $this;
+ }
+
+ /**
+ * Run the pipeline with a final destination callback.
+ */
+ public function then(Closure $destination)
+ {
+ $pipeline = array_reduce(array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination));
+
+ return $pipeline($this->passable);
+ }
+
+ /**
+ * Get the final piece of the Closure onion.
+ */
+ protected function prepareDestination(Closure $destination): Closure
+ {
+ return static function ($passable) use ($destination) {
+ return $destination($passable);
+ };
+ }
+
+ /**
+ * Get a Closure that represents a slice of the application onion.
+ */
+ protected function carry(): Closure
+ {
+ return function ($stack, $pipe) {
+ return function ($passable) use ($stack, $pipe) {
+ if (is_callable($pipe)) {
+ // If the pipe is an instance of a Closure, we will just call it directly but
+ // otherwise we'll resolve the pipes out of the container and call it with
+ // the appropriate method and arguments, returning the results back out.
+ return $pipe($passable, $stack);
+ }
+ if (!is_object($pipe)) {
+ [$name, $parameters] = $this->parsePipeString($pipe);
+
+ // If the pipe is a string we will parse the string and resolve the class out
+ // of the dependency injection container. We can then build a callable and
+ // execute the pipe function giving in the parameters that are required.
+ $pipe = $this->container->get($name);
+
+ $parameters = array_merge([$passable, $stack], $parameters);
+ } else {
+ // If the pipe is already an object we'll just make a callable and pass it to
+ // the pipe as-is. There is no need to do any extra parsing and formatting
+ // since the object we're given was already a fully instantiated object.
+ $parameters = [$passable, $stack];
+ }
+
+ $carry = method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters);
+
+ return $this->handleCarry($carry);
+ };
+ };
+ }
+
+ /**
+ * Parse full pipe string to get name and parameters.
+ */
+ protected function parsePipeString(string $pipe): array
+ {
+ [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
+
+ if (is_string($parameters)) {
+ $parameters = explode(',', $parameters);
+ }
+
+ return [$name, $parameters];
+ }
+
+ /**
+ * Handle the value returned from each pipe before passing it to the next.
+ *
+ * @param mixed $carry
+ *
+ * @return mixed
+ */
+ protected function handleCarry($carry)
+ {
+ return $carry;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Str.php b/server/addons/epay/library/v3/Yansongda/Supports/Str.php
new file mode 100644
index 0000000..a2a19d3
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Str.php
@@ -0,0 +1,587 @@
+ $val) {
+ $value = str_replace($val, $key, $value);
+ }
+
+ return preg_replace('/[^\x20-\x7E]/u', '', $value);
+ }
+
+ /**
+ * Get the portion of a string before a given value.
+ */
+ public static function before(string $subject, string $search): string
+ {
+ return '' === $search ? $subject : explode($search, $subject)[0];
+ }
+
+ /**
+ * Convert a value to camel case.
+ */
+ public static function camel(string $value): string
+ {
+ if (isset(static::$camelCache[$value])) {
+ return static::$camelCache[$value];
+ }
+
+ return static::$camelCache[$value] = lcfirst(static::studly($value));
+ }
+
+ /**
+ * Determine if a given string contains a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function contains(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if ('' !== $needle && false !== mb_strpos($haystack, $needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if a given string ends with a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function endsWith(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if (substr($haystack, -strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Cap a string with a single instance of a given value.
+ */
+ public static function finish(string $value, string $cap): string
+ {
+ $quoted = preg_quote($cap, '/');
+
+ return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
+ }
+
+ /**
+ * Determine if a given string matches a given pattern.
+ *
+ * @param string|array $pattern
+ */
+ public static function is($pattern, string $value): bool
+ {
+ $patterns = Arr::wrap($pattern);
+
+ if (empty($patterns)) {
+ return false;
+ }
+
+ foreach ($patterns as $pattern) {
+ // If the given value is an exact match we can of course return true right
+ // from the beginning. Otherwise, we will translate asterisks and do an
+ // actual pattern match against the two strings to see if they match.
+ if ($pattern == $value) {
+ return true;
+ }
+
+ $pattern = preg_quote($pattern, '#');
+
+ // Asterisks are translated into zero-or-more regular expression wildcards
+ // to make it convenient to check if the strings starts with the given
+ // pattern such as "library/*", making any string check convenient.
+ $pattern = str_replace('\*', '.*', $pattern);
+
+ if (1 === preg_match('#^'.$pattern.'\z#u', $value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a string to kebab case.
+ */
+ public static function kebab(string $value): string
+ {
+ return static::snake($value, '-');
+ }
+
+ /**
+ * Return the length of the given string.
+ */
+ public static function length(string $value, ?string $encoding = null): int
+ {
+ if (null !== $encoding) {
+ return mb_strlen($value, $encoding);
+ }
+
+ return mb_strlen($value);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ */
+ public static function limit(string $value, int $limit = 100, string $end = '...'): string
+ {
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
+ return $value;
+ }
+
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ */
+ public static function lower(string $value): string
+ {
+ return mb_strtolower($value, 'UTF-8');
+ }
+
+ /**
+ * Limit the number of words in a string.
+ */
+ public static function words(string $value, int $words = 100, string $end = '...'): string
+ {
+ preg_match('/^\s*+\S++\s*+{1,'.$words.'}/u', $value, $matches);
+
+ if (!isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+ return $value;
+ }
+
+ return rtrim($matches[0]).$end;
+ }
+
+ /**
+ * Parse a Class.
+ */
+ public static function parseCallback(string $callback, ?string $default = null): array
+ {
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+ }
+
+ /**
+ * Generate a more truly "random" alpha-numeric string.
+ *
+ * @throws Exception
+ */
+ public static function random(int $length = 16): string
+ {
+ $string = '';
+
+ while (($len = strlen($string)) < $length) {
+ $size = $length - $len;
+
+ $bytes = random_bytes($size);
+
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+ }
+
+ return $string;
+ }
+
+ public static function uuidV4(): string
+ {
+ return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+
+ // 32 bits for "time_low"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+
+ // 16 bits for "time_mid"
+ mt_rand(0, 0xffff),
+
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand(0, 0x0fff) | 0x4000,
+
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand(0, 0x3fff) | 0x8000,
+
+ // 48 bits for "node"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+ );
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ */
+ public static function replaceArray(string $search, array $replace, string $subject): string
+ {
+ foreach ($replace as $value) {
+ $subject = static::replaceFirst($search, $value, $subject);
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ */
+ public static function replaceFirst(string $search, string $replace, string $subject): string
+ {
+ if ('' == $search) {
+ return $subject;
+ }
+
+ $position = strpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ */
+ public static function replaceLast(string $search, string $replace, string $subject): string
+ {
+ $position = strrpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ */
+ public static function start(string $value, string $prefix): string
+ {
+ $quoted = preg_quote($prefix, '/');
+
+ return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ */
+ public static function upper(string $value): string
+ {
+ return mb_strtoupper($value, 'UTF-8');
+ }
+
+ /**
+ * Convert the given string to title case.
+ */
+ public static function title(string $value): string
+ {
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ */
+ public static function slug(string $title, string $separator = '-', string $language = 'en'): string
+ {
+ $title = static::ascii($title, $language);
+
+ // Convert all dashes/underscores into separator
+ $flip = '-' == $separator ? '_' : '-';
+
+ $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+ // Replace @ with the word 'at'
+ $title = str_replace('@', $separator.'at'.$separator, $title);
+
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
+ $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+ return trim($title, $separator);
+ }
+
+ /**
+ * Convert a string to snake case.
+ */
+ public static function snake(string $value, string $delimiter = '_'): string
+ {
+ $key = $value;
+
+ if (isset(static::$snakeCache[$key][$delimiter])) {
+ return static::$snakeCache[$key][$delimiter];
+ }
+
+ if (!ctype_lower($value)) {
+ $value = preg_replace('/\s+/u', '', ucwords($value));
+
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+ }
+
+ return static::$snakeCache[$key][$delimiter] = $value;
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param string|array $needles
+ */
+ public static function startsWith(string $haystack, $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if ('' !== $needle && substr($haystack, 0, strlen($needle)) === (string) $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ */
+ public static function studly(string $value): string
+ {
+ $key = $value;
+
+ if (isset(static::$studlyCache[$key])) {
+ return static::$studlyCache[$key];
+ }
+
+ $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+ return static::$studlyCache[$key] = str_replace(' ', '', $value);
+ }
+
+ /**
+ * Returns the portion of string specified by the start and length parameters.
+ */
+ public static function substr(string $string, int $start, ?int $length = null): string
+ {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ */
+ public static function ucfirst(string $string): string
+ {
+ return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+ }
+
+ /**
+ * Convert string's encoding.
+ */
+ public static function encoding(string $string, string $to = 'utf-8', string $from = 'gb2312'): string
+ {
+ return mb_convert_encoding($string, $to, $from);
+ }
+
+ /**
+ * Returns the replacements for the ascii method.
+ *
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function charsArray(): array
+ {
+ static $charsArray;
+
+ if (isset($charsArray)) {
+ return $charsArray;
+ }
+
+ return $charsArray = [
+ '0' => ['°', '₀', '۰', '0'],
+ '1' => ['¹', '₁', '۱', '1'],
+ '2' => ['²', '₂', '۲', '2'],
+ '3' => ['³', '₃', '۳', '3'],
+ '4' => ['⁴', '₄', '۴', '٤', '4'],
+ '5' => ['⁵', '₅', '۵', '٥', '5'],
+ '6' => ['⁶', '₆', '۶', '٦', '6'],
+ '7' => ['⁷', '₇', '۷', '7'],
+ '8' => ['⁸', '₈', '۸', '8'],
+ '9' => ['⁹', '₉', '۹', '9'],
+ 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'],
+ 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
+ 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
+ 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
+ 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
+ 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
+ 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
+ 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
+ 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i'],
+ 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
+ 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
+ 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
+ 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
+ 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
+ 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
+ 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
+ 'q' => ['ყ', 'q'],
+ 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
+ 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
+ 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
+ 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
+ 'v' => ['в', 'ვ', 'ϐ', 'v'],
+ 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
+ 'x' => ['χ', 'ξ', 'x'],
+ 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
+ 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
+ 'aa' => ['ع', 'आ', 'آ'],
+ 'ae' => ['æ', 'ǽ'],
+ 'ai' => ['ऐ'],
+ 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
+ 'dj' => ['ђ', 'đ'],
+ 'dz' => ['џ', 'ძ'],
+ 'ei' => ['ऍ'],
+ 'gh' => ['غ', 'ღ'],
+ 'ii' => ['ई'],
+ 'ij' => ['ij'],
+ 'kh' => ['х', 'خ', 'ხ'],
+ 'lj' => ['љ'],
+ 'nj' => ['њ'],
+ 'oe' => ['ö', 'œ', 'ؤ'],
+ 'oi' => ['ऑ'],
+ 'oii' => ['ऒ'],
+ 'ps' => ['ψ'],
+ 'sh' => ['ш', 'შ', 'ش'],
+ 'shch' => ['щ'],
+ 'ss' => ['ß'],
+ 'sx' => ['ŝ'],
+ 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
+ 'ts' => ['ц', 'ც', 'წ'],
+ 'ue' => ['ü'],
+ 'uu' => ['ऊ'],
+ 'ya' => ['я'],
+ 'yu' => ['ю'],
+ 'zh' => ['ж', 'ჟ', 'ژ'],
+ '(c)' => ['©'],
+ 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
+ 'B' => ['Б', 'Β', 'ब', 'B'],
+ 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
+ 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
+ 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
+ 'F' => ['Ф', 'Φ', 'F'],
+ 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
+ 'H' => ['Η', 'Ή', 'Ħ', 'H'],
+ 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
+ 'J' => ['J'],
+ 'K' => ['К', 'Κ', 'K'],
+ 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
+ 'M' => ['М', 'Μ', 'M'],
+ 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
+ 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
+ 'P' => ['П', 'Π', 'P'],
+ 'Q' => ['Q'],
+ 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
+ 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
+ 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
+ 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
+ 'V' => ['В', 'V'],
+ 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
+ 'X' => ['Χ', 'Ξ', 'X'],
+ 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
+ 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
+ 'AE' => ['Æ', 'Ǽ'],
+ 'Ch' => ['Ч'],
+ 'Dj' => ['Ђ'],
+ 'Dz' => ['Џ'],
+ 'Gx' => ['Ĝ'],
+ 'Hx' => ['Ĥ'],
+ 'Ij' => ['IJ'],
+ 'Jx' => ['Ĵ'],
+ 'Kh' => ['Х'],
+ 'Lj' => ['Љ'],
+ 'Nj' => ['Њ'],
+ 'Oe' => ['Œ'],
+ 'Ps' => ['Ψ'],
+ 'Sh' => ['Ш'],
+ 'Shch' => ['Щ'],
+ 'Ss' => ['ẞ'],
+ 'Th' => ['Þ'],
+ 'Ts' => ['Ц'],
+ 'Ya' => ['Я'],
+ 'Yu' => ['Ю'],
+ 'Zh' => ['Ж'],
+ ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
+ ];
+ }
+
+ /**
+ * Returns the language specific replacements for the ascii method.
+ *
+ * Note: Adapted from Stringy\Stringy.
+ *
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function languageSpecificCharsArray(string $language): ?array
+ {
+ static $languageSpecific;
+ if (!isset($languageSpecific)) {
+ $languageSpecific = [
+ 'bg' => [
+ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
+ ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
+ ],
+ 'de' => [
+ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
+ ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
+ ],
+ ];
+ }
+
+ return $languageSpecific[$language] ?? null;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Traits/Accessable.php b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Accessable.php
new file mode 100644
index 0000000..8e5abd8
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Accessable.php
@@ -0,0 +1,146 @@
+get($key);
+ }
+
+ /**
+ * Whether or not an data exists by key.
+ */
+ public function __isset(string $key): bool
+ {
+ return !is_null($this->get($key));
+ }
+
+ /**
+ * Unsets an data by key.
+ */
+ public function __unset(string $key)
+ {
+ $this->offsetUnset($key);
+ }
+
+ /**
+ * __set.
+ *
+ * @param mixed $value
+ */
+ public function __set(string $key, $value): void
+ {
+ $this->set($key, $value);
+ }
+
+ /**
+ * get.
+ *
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get(?string $key = null, $default = null)
+ {
+ if (is_null($key)) {
+ return method_exists($this, 'toArray') ? $this->toArray() : $default;
+ }
+
+ $method = 'get'.Str::studly($key);
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ return $default;
+ }
+
+ /**
+ * set.
+ *
+ * @param mixed $value
+ */
+ public function set(string $key, $value): self
+ {
+ $method = 'set'.Str::studly($key);
+
+ if (method_exists($this, $method)) {
+ $this->{$method}($value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Whether a offset exists.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetexists.php
+ *
+ * @param mixed $offset an offset to check for
+ *
+ * @return bool true on success or false on failure.
+ *
+ * The return value will be casted to boolean if non-boolean was returned.
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($offset)
+ {
+ return !is_null($this->get($offset));
+ }
+
+ /**
+ * Offset to retrieve.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetget.php
+ *
+ * @param mixed $offset the offset to retrieve
+ *
+ * @return mixed can return all value types
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ /**
+ * Offset to set.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetset.php
+ *
+ * @param mixed $offset the offset to assign the value to
+ * @param mixed $value the value to set
+ *
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value)
+ {
+ $this->set($offset, $value);
+ }
+
+ /**
+ * Offset to unset.
+ *
+ * @see https://php.net/manual/en/arrayaccess.offsetunset.php
+ *
+ * @param mixed $offset the offset to unset
+ *
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset)
+ {
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Traits/Arrayable.php b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Arrayable.php
new file mode 100644
index 0000000..973e02a
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Arrayable.php
@@ -0,0 +1,28 @@
+getProperties() as $item) {
+ $k = $item->getName();
+ $method = 'get'.Str::studly($k);
+
+ $result[Str::snake($k)] = method_exists($this, $method) ? $this->{$method}() : $this->{$k};
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v3/Yansongda/Supports/Traits/Serializable.php b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Serializable.php
new file mode 100644
index 0000000..1cace58
--- /dev/null
+++ b/server/addons/epay/library/v3/Yansongda/Supports/Traits/Serializable.php
@@ -0,0 +1,65 @@
+toArray();
+ }
+
+ return [];
+ }
+
+ public function __unserialize(array $data): void
+ {
+ $this->unserializeArray($data);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ public function serialize(): ?string
+ {
+ return serialize($this);
+ }
+
+ public function unserialize($data): void
+ {
+ unserialize($data);
+ }
+
+ /**
+ * toJson.
+ */
+ public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+ {
+ return json_encode($this->__serialize(), $option);
+ }
+
+ /**
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->__serialize();
+ }
+
+ public function unserializeArray(array $data): self
+ {
+ foreach ($data as $key => $item) {
+ if (method_exists($this, 'set')) {
+ $this->set($key, $item);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/server/addons/epay/library/v3/version.txt b/server/addons/epay/library/v3/version.txt
new file mode 100644
index 0000000..de1eb44
--- /dev/null
+++ b/server/addons/epay/library/v3/version.txt
@@ -0,0 +1 @@
+v3.4.1
\ No newline at end of file
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Artful.php b/server/addons/epay/library/v3new/Yansongda/Artful/Artful.php
new file mode 100644
index 0000000..cf9ba48
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Artful.php
@@ -0,0 +1,342 @@
+registerServices($config, $container);
+
+ Artful::set(DirectionInterface::class, CollectionDirection::class);
+ Artful::set(PackerInterface::class, JsonPacker::class);
+ }
+
+ /**
+ * @return mixed
+ *
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ public static function __callStatic(string $service, array $config)
+ {
+ if (!empty($config)) {
+ self::config(...$config);
+ }
+
+ return self::get($service);
+ }
+
+ /**
+ * @throws ContainerException
+ */
+ public static function config(array $config = [], null|Closure|ContainerInterface $container = null): bool
+ {
+ if (self::hasContainer() && !($config['_force'] ?? false)) {
+ return false;
+ }
+
+ new self($config, $container);
+
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ *
+ * @throws ContainerException
+ */
+ public static function set(string $name, mixed $value): void
+ {
+ try {
+ $container = Artful::getContainer();
+
+ if ($container instanceof LaravelContainer) {
+ $container->singleton($name, $value instanceof Closure ? $value : static fn () => $value);
+
+ return;
+ }
+
+ if (method_exists($container, 'set')) {
+ $container->set(...func_get_args());
+
+ return;
+ }
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException('容器异常: '.$e->getMessage());
+ }
+
+ throw new ContainerException('容器异常: 当前容器类型不支持 `set` 方法');
+ }
+
+ /**
+ * @codeCoverageIgnore
+ *
+ * @throws ContainerException
+ */
+ public static function make(string $service, array $parameters = []): mixed
+ {
+ try {
+ $container = Artful::getContainer();
+
+ if (method_exists($container, 'make')) {
+ return $container->make(...func_get_args());
+ }
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException('容器异常: '.$e->getMessage());
+ }
+
+ $parameters = array_values($parameters);
+
+ return new $service(...$parameters);
+ }
+
+ /**
+ * @throws ServiceNotFoundException
+ * @throws ContainerException
+ */
+ public static function get(string $service): mixed
+ {
+ try {
+ return Artful::getContainer()->get($service);
+ } catch (NotFoundExceptionInterface $e) {
+ throw new ServiceNotFoundException('服务未找到: '.$e->getMessage());
+ } catch (ContainerNotFoundException $e) {
+ throw $e;
+ } catch (Throwable $e) {
+ throw new ContainerException('容器异常: '.$e->getMessage());
+ }
+ }
+
+ /**
+ * @throws ContainerNotFoundException
+ */
+ public static function has(string $service): bool
+ {
+ return Artful::getContainer()->has($service);
+ }
+
+ public static function setContainer(null|Closure|ContainerInterface $container): void
+ {
+ self::$container = $container;
+ }
+
+ /**
+ * @throws ContainerNotFoundException
+ */
+ public static function getContainer(): ContainerInterface
+ {
+ if (self::$container instanceof ContainerInterface) {
+ return self::$container;
+ }
+
+ if (self::$container instanceof Closure) {
+ return (self::$container)();
+ }
+
+ throw new ContainerNotFoundException('容器未找到: `getContainer()` 方法调用失败! 或许你应该先 `setContainer()`');
+ }
+
+ public static function hasContainer(): bool
+ {
+ return self::$container instanceof ContainerInterface || self::$container instanceof Closure;
+ }
+
+ public static function clear(): void
+ {
+ self::$container = null;
+ }
+
+ /**
+ * @throws ContainerException
+ */
+ public static function load(string $service, mixed $data = null): void
+ {
+ self::registerService($service, $data);
+ }
+
+ /**
+ * @throws ContainerException
+ */
+ public static function registerService(string $service, mixed $data = null): void
+ {
+ $var = new $service();
+
+ if ($var instanceof ServiceProviderInterface) {
+ $var->register($data);
+ }
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public static function shortcut(string $shortcut, array $params = []): null|Collection|MessageInterface|Rocket
+ {
+ if (!class_exists($shortcut) || !in_array(ShortcutInterface::class, class_implements($shortcut))) {
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_INVALID, "参数异常: [{$shortcut}] 未实现 `ShortcutInterface`");
+ }
+
+ /* @var ShortcutInterface $shortcutInstance */
+ $shortcutInstance = self::get($shortcut);
+
+ return self::artful($shortcutInstance->getPlugins($params), $params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public static function artful(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ Event::dispatch(new Event\ArtfulStart($plugins, $params));
+
+ self::verifyPlugin($plugins);
+
+ /* @var Pipeline $pipeline */
+ $pipeline = self::make(Pipeline::class);
+
+ /* @var Rocket $rocket */
+ $rocket = $pipeline
+ ->send((new Rocket())->setParams($params)->setPayload(new Collection()))
+ ->through($plugins)
+ ->via('assembly')
+ ->then(static fn ($rocket) => self::ignite($rocket));
+
+ Event::dispatch(new Event\ArtfulEnd($rocket));
+
+ if (!empty($params['_return_rocket'])) {
+ return $rocket;
+ }
+
+ return $rocket->getDestination();
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws InvalidResponseException
+ * @throws ServiceNotFoundException
+ */
+ public static function ignite(Rocket $rocket): Rocket
+ {
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ /* @var HttpClientFactoryInterface $httpFactory */
+ $httpFactory = self::get(HttpClientFactoryInterface::class);
+ /* @var Config $config */
+ $config = self::get(ConfigInterface::class);
+
+ if (!$httpFactory instanceof HttpClientFactoryInterface) {
+ throw new InvalidParamsException(Exception::PARAMS_HTTP_CLIENT_FACTORY_INVALID, '参数异常: 配置的 `HttpClientFactoryInterface` 不符合规范');
+ }
+
+ Logger::info('[Artful] 准备请求第三方 API', $rocket->toArray());
+
+ $http = $httpFactory->create(array_merge($config->get('http', []), $rocket->getPayload()?->get('_http') ?? []));
+
+ Event::dispatch(new Event\HttpStart($rocket));
+
+ try {
+ $response = $http->sendRequest($rocket->getRadar());
+
+ $rocket->setDestination(clone $response)
+ ->setDestinationOrigin(clone $response);
+ } catch (Throwable $e) {
+ Logger::error('[Artful] 请求第三方 API 出错', ['message' => $e->getMessage(), 'rocket' => $rocket->toArray(), 'trace' => $e->getTrace()]);
+
+ throw new InvalidResponseException(Exception::REQUEST_RESPONSE_ERROR, '响应异常: 请求第三方 API 出错 - '.$e->getMessage(), [], $e);
+ }
+
+ Logger::info('[Artful] 请求第三方 API 成功', ['rocket' => $rocket->toArray()]);
+
+ Event::dispatch(new Event\HttpEnd($rocket));
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected static function verifyPlugin(array $plugins): void
+ {
+ foreach ($plugins as $plugin) {
+ if (is_callable($plugin)) {
+ continue;
+ }
+
+ if ((is_object($plugin)
+ || (is_string($plugin) && class_exists($plugin)))
+ && in_array(PluginInterface::class, class_implements($plugin))) {
+ continue;
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_INCOMPATIBLE, "参数异常: [{$plugin}] 插件未实现 `PluginInterface`");
+ }
+ }
+
+ /**
+ * @throws ContainerException
+ */
+ private function registerServices(array $config, null|Closure|ContainerInterface $container = null): void
+ {
+ foreach ($this->coreService as $service) {
+ self::registerService($service, ContainerServiceProvider::class == $service ? $container : $config);
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Contract/ConfigInterface.php b/server/addons/epay/library/v3new/Yansongda/Artful/Contract/ConfigInterface.php
new file mode 100644
index 0000000..80916d5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Contract/ConfigInterface.php
@@ -0,0 +1,14 @@
+getBody();
+
+ if (!is_null($result = $packer->unpack($body, $params))) {
+ return new Collection($result);
+ }
+
+ throw new InvalidResponseException(Exception::RESPONSE_UNPACK_ERROR, '响应异常: 解包错误', ['body' => $body, 'response' => $response]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Direction/NoHttpRequestDirection.php b/server/addons/epay/library/v3new/Yansongda/Artful/Direction/NoHttpRequestDirection.php
new file mode 100644
index 0000000..8201c90
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Direction/NoHttpRequestDirection.php
@@ -0,0 +1,17 @@
+{$method}(...$args);
+
+ return;
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_EVENT_DRIVER_INVALID, '参数异常: 配置的 `EventDispatcherInterface` 不符合 PSR 规范');
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Event/ArtfulEnd.php b/server/addons/epay/library/v3new/Yansongda/Artful/Event/ArtfulEnd.php
new file mode 100644
index 0000000..b59ca80
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Event/ArtfulEnd.php
@@ -0,0 +1,7 @@
+extra = $extra;
+
+ parent::__construct($message, $code, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Exception/InvalidConfigException.php b/server/addons/epay/library/v3new/Yansongda/Artful/Exception/InvalidConfigException.php
new file mode 100644
index 0000000..838c558
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Exception/InvalidConfigException.php
@@ -0,0 +1,18 @@
+response = $extra;
+
+ parent::__construct($message, $code, $extra, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Exception/ServiceNotFoundException.php b/server/addons/epay/library/v3new/Yansongda/Artful/Exception/ServiceNotFoundException.php
new file mode 100644
index 0000000..20824d0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Exception/ServiceNotFoundException.php
@@ -0,0 +1,16 @@
+filter(static fn ($v, $k) => !Str::startsWith($k, '_') && !is_null($v) && (empty($closure) || $closure($k, $v)));
+}
+
+function get_radar_method(?Collection $payload): ?string
+{
+ $string = $payload?->get('_method') ?? null;
+
+ if (is_null($string)) {
+ return null;
+ }
+
+ return strtoupper($string);
+}
+
+function get_radar_url(?Collection $payload): ?string
+{
+ return $payload?->get('_url') ?? null;
+}
+
+function get_radar_body(?Collection $payload): mixed
+{
+ return $payload?->get('_body') ?? null;
+}
+
+function get_radar_headers(?Collection $payload): mixed
+{
+ return $payload?->get('_headers') ?? null;
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/HttpClientFactory.php b/server/addons/epay/library/v3new/Yansongda/Artful/HttpClientFactory.php
new file mode 100644
index 0000000..c162f6e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/HttpClientFactory.php
@@ -0,0 +1,39 @@
+container->has(HttpClientInterface::class)) {
+ if (($http = $this->container->get(HttpClientInterface::class)) instanceof ClientInterface) {
+ return $http;
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_HTTP_CLIENT_INVALID, '参数异常: `HttpClient` 不符合 PSR 规范');
+ }
+
+ return new Client($options);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Logger.php b/server/addons/epay/library/v3new/Yansongda/Artful/Logger.php
new file mode 100644
index 0000000..2b3c6dd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Logger.php
@@ -0,0 +1,49 @@
+get('logger.enable', false)) {
+ return;
+ }
+
+ $class = Artful::get(LoggerInterface::class);
+
+ if ($class instanceof \Psr\Log\LoggerInterface) {
+ $class->{$method}(...$args);
+
+ return;
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_LOGGER_DRIVER_INVALID, '配置异常: 配置的 `LoggerInterface` 不符合 PSR 规范');
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Packer/JsonPacker.php b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/JsonPacker.php
new file mode 100644
index 0000000..39ed979
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/JsonPacker.php
@@ -0,0 +1,26 @@
+isEmpty()) || empty($payload)) {
+ return '';
+ }
+
+ return Collection::wrap($payload)->toJson();
+ }
+
+ public function unpack(string $payload, ?array $params = null): ?array
+ {
+ return Arr::wrapJson($payload);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Packer/QueryPacker.php b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/QueryPacker.php
new file mode 100644
index 0000000..607049d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/QueryPacker.php
@@ -0,0 +1,22 @@
+query();
+ }
+
+ public function unpack(string $payload, ?array $params = null): array
+ {
+ return Arr::wrapQuery($payload, !empty($params['_unpack_raw']));
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Packer/XmlPacker.php b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/XmlPacker.php
new file mode 100644
index 0000000..2aeaea9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Packer/XmlPacker.php
@@ -0,0 +1,22 @@
+toXml();
+ }
+
+ public function unpack(string $payload, ?array $params = null): array
+ {
+ return Arr::wrapXml($payload);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddPayloadBodyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddPayloadBodyPlugin.php
new file mode 100644
index 0000000..3affeae
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddPayloadBodyPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $packer = get_packer($rocket->getPacker());
+
+ $rocket->mergePayload(['_body' => $packer->pack(filter_params($rocket->getPayload()))]);
+
+ Logger::info('[AddPayloadBodyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddRadarPlugin.php
new file mode 100644
index 0000000..f842f4d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/AddRadarPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ $rocket->setRadar(new Request(
+ get_radar_method($payload) ?? 'POST',
+ get_radar_url($payload),
+ get_radar_headers($payload) ?? $this->getHeaders(),
+ get_radar_body($payload),
+ ));
+
+ Logger::info('[AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'User-Agent' => 'yansongda/artful-v1',
+ 'Content-Type' => 'application/json;charset=utf-8',
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/ParserPlugin.php b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/ParserPlugin.php
new file mode 100644
index 0000000..daa9701
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/ParserPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $response = $rocket->getDestination();
+ $direction = get_direction($rocket->getDirection());
+ $packer = get_packer($rocket->getPacker());
+ $payload = $rocket->getPayload();
+
+ if (!is_null($response) && !($response instanceof ResponseInterface)) {
+ throw new InvalidParamsException(Exception::PARAMS_PARSER_DIRECTION_INVALID, '参数异常: 解析插件中 `Rocket` 的 `destination` 只能是 `null` 或者 `ResponseInterface`');
+ }
+
+ $rocket->setDestination($direction->guide($packer, $response, $payload?->all() ?? []));
+
+ Logger::debug('[ParserPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/StartPlugin.php
new file mode 100644
index 0000000..f32f2ee
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Plugin/StartPlugin.php
@@ -0,0 +1,24 @@
+ $rocket]);
+
+ $rocket->mergePayload($rocket->getParams());
+
+ Logger::info('[StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Rocket.php b/server/addons/epay/library/v3new/Yansongda/Artful/Rocket.php
new file mode 100644
index 0000000..97b39fd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Rocket.php
@@ -0,0 +1,180 @@
+radar;
+ }
+
+ public function setRadar(?RequestInterface $radar): Rocket
+ {
+ $this->radar = $radar;
+
+ return $this;
+ }
+
+ public function getParams(): array
+ {
+ return $this->params;
+ }
+
+ public function setParams(array $params): Rocket
+ {
+ $this->params = $params;
+
+ return $this;
+ }
+
+ public function mergeParams(array $params): Rocket
+ {
+ $this->params = array_merge($this->params, $params);
+
+ return $this;
+ }
+
+ public function getPayload(): ?Collection
+ {
+ return $this->payload;
+ }
+
+ public function setPayload(null|array|Collection $payload): Rocket
+ {
+ if (is_array($payload)) {
+ $payload = new Collection($payload);
+ }
+
+ $this->payload = $payload;
+
+ return $this;
+ }
+
+ public function mergePayload(array $payload): Rocket
+ {
+ if (empty($this->payload)) {
+ $this->payload = new Collection();
+ }
+
+ $this->payload = $this->payload->merge($payload);
+
+ return $this;
+ }
+
+ public function exceptPayload(mixed $key): Rocket
+ {
+ if (empty($this->payload)) {
+ return $this;
+ }
+
+ $this->payload = $this->payload->except($key);
+
+ return $this;
+ }
+
+ public function getPacker(): string
+ {
+ return $this->packer;
+ }
+
+ public function setPacker(string $packer): Rocket
+ {
+ $this->packer = $packer;
+
+ return $this;
+ }
+
+ public function getDirection(): string
+ {
+ return $this->direction;
+ }
+
+ public function setDirection(string $direction): Rocket
+ {
+ $this->direction = $direction;
+
+ return $this;
+ }
+
+ public function getDestination(): null|Collection|MessageInterface
+ {
+ return $this->destination;
+ }
+
+ public function setDestination(null|Collection|MessageInterface $destination): Rocket
+ {
+ $this->destination = $destination;
+
+ return $this;
+ }
+
+ public function getDestinationOrigin(): null|RequestInterface|ResponseInterface
+ {
+ return $this->destinationOrigin;
+ }
+
+ public function setDestinationOrigin(null|RequestInterface|ResponseInterface $destinationOrigin): Rocket
+ {
+ $this->destinationOrigin = $destinationOrigin;
+
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $request = $this->getRadar();
+ $destination = $this->getDestinationOrigin();
+
+ return [
+ 'radar' => [
+ 'url' => $request?->getUri()->__toString(),
+ 'method' => $request?->getMethod(),
+ 'headers' => $request?->getHeaders(),
+ 'body' => (string) $request?->getBody(),
+ ],
+ 'params' => $this->getParams(),
+ 'payload' => $this->getPayload()?->toArray(),
+ 'packer' => $this->getPacker(),
+ 'direction' => $this->getDirection(),
+ 'destination' => $this->getDestination(),
+ 'destination_origin' => [
+ 'status' => $destination instanceof ResponseInterface ? $destination->getStatusCode() : null,
+ 'headers' => $destination?->getHeaders(),
+ 'body' => (string) $destination?->getBody(),
+ ],
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Service/ConfigServiceProvider.php b/server/addons/epay/library/v3new/Yansongda/Artful/Service/ConfigServiceProvider.php
new file mode 100644
index 0000000..3a6ff5e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Service/ConfigServiceProvider.php
@@ -0,0 +1,42 @@
+ [
+ 'enable' => false,
+ 'file' => null,
+ 'identify' => 'yansongda.artful',
+ 'level' => 'debug',
+ 'type' => 'daily',
+ 'max_files' => 30,
+ ],
+ 'http' => [
+ 'timeout' => 5.0,
+ 'connect_timeout' => 3.0,
+ 'headers' => [
+ 'User-Agent' => 'yansongda/artful-v1',
+ ],
+ ],
+ ];
+
+ /**
+ * @throws ContainerException
+ */
+ public function register(mixed $data = null): void
+ {
+ $config = new Config(array_replace_recursive($this->config, $data ?? []));
+
+ Artful::set(ConfigInterface::class, $config);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Service/ContainerServiceProvider.php b/server/addons/epay/library/v3new/Yansongda/Artful/Service/ContainerServiceProvider.php
new file mode 100644
index 0000000..5083298
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Service/ContainerServiceProvider.php
@@ -0,0 +1,108 @@
+ LaravelContainer::class,
+ 'hyperf' => HyperfContainer::class,
+ ];
+
+ /**
+ * @throws ContainerException
+ */
+ public function register(mixed $data = null): void
+ {
+ if ($data instanceof ContainerInterface || $data instanceof Closure) {
+ Artful::setContainer($data);
+
+ return;
+ }
+
+ if (Artful::hasContainer()) {
+ return;
+ }
+
+ foreach ($this->detectApplication as $framework => $application) {
+ $method = $framework.'Application';
+
+ if (class_exists($application) && method_exists($this, $method) && $this->{$method}()) {
+ return;
+ }
+ }
+
+ $this->defaultApplication();
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ContainerNotFoundException
+ */
+ protected function laravelApplication(): bool
+ {
+ Artful::setContainer(static fn () => LaravelContainer::getInstance());
+
+ Artful::set(\Yansongda\Artful\Contract\ContainerInterface::class, LaravelContainer::getInstance());
+
+ if (!Artful::has(ContainerInterface::class)) {
+ Artful::set(ContainerInterface::class, LaravelContainer::getInstance());
+ }
+
+ return true;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ContainerNotFoundException
+ */
+ protected function hyperfApplication(): bool
+ {
+ if (!HyperfContainer::hasContainer()) {
+ return false;
+ }
+
+ Artful::setContainer(static fn () => HyperfContainer::getContainer());
+
+ Artful::set(\Yansongda\Artful\Contract\ContainerInterface::class, HyperfContainer::getContainer());
+
+ if (!Artful::has(ContainerInterface::class)) {
+ Artful::set(ContainerInterface::class, HyperfContainer::getContainer());
+ }
+
+ return true;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ContainerNotFoundException
+ */
+ protected function defaultApplication(): void
+ {
+ if (!class_exists(DefaultContainer::class)) {
+ throw new ContainerNotFoundException('容器未找到: Init failed! Maybe you should install `hyperf/pimple` first', Exception::CONTAINER_NOT_FOUND);
+ }
+
+ $container = (new DefaultContainer())();
+
+ Artful::setContainer($container);
+
+ Artful::set(\Yansongda\Artful\Contract\ContainerInterface::class, $container);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Artful/Service/EventServiceProvider.php b/server/addons/epay/library/v3new/Yansongda/Artful/Service/EventServiceProvider.php
new file mode 100644
index 0000000..de89a57
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Artful/Service/EventServiceProvider.php
@@ -0,0 +1,24 @@
+ false,
+ 'file' => null,
+ 'identify' => 'yansongda.artful',
+ 'level' => 'DEBUG',
+ 'type' => 'daily',
+ 'max_files' => 30,
+ 'formatter' => "%datetime% > %channel%.%level_name% > %message% %context% %extra%\n\n",
+ ];
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ public function register(mixed $data = null): void
+ {
+ /* @var ConfigInterface $config */
+ $config = Artful::get(ConfigInterface::class);
+
+ $this->config = array_merge($this->config, $config->get('logger', []));
+
+ if (class_exists(Logger::class) && (true === ($this->config['enable'] ?? false))) {
+ $logger = new Logger($this->config['identify']);
+
+ $logger->pushHandler($this->getDefaultHandler()
+ ->setFormatter($this->getDefaultFormatter()));
+
+ Artful::set(LoggerInterface::class, $logger);
+ }
+ }
+
+ protected function getDefaultFormatter(): LineFormatter
+ {
+ return new LineFormatter(
+ $this->config['formatter'],
+ null,
+ false,
+ true
+ );
+ }
+
+ protected function getDefaultHandler(): AbstractProcessingHandler
+ {
+ $file = $this->config['file'] ?? (sys_get_temp_dir().'/logs/'.$this->config['identify'].'.log');
+
+ return match ($this->config['type']) {
+ 'single' => new StreamHandler($file, $this->config['level']),
+ default => new RotatingFileHandler($file, $this->config['max_files'], $this->config['level']),
+ };
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Contract/ProviderInterface.php b/server/addons/epay/library/v3new/Yansongda/Pay/Contract/ProviderInterface.php
new file mode 100644
index 0000000..018d715
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Contract/ProviderInterface.php
@@ -0,0 +1,28 @@
+provider = $provider;
+ $this->contents = $contents;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Event/MethodCalled.php b/server/addons/epay/library/v3new/Yansongda/Pay/Event/MethodCalled.php
new file mode 100644
index 0000000..2836f27
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Event/MethodCalled.php
@@ -0,0 +1,26 @@
+provider = $provider;
+ $this->name = $name;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Event/PayEnd.php b/server/addons/epay/library/v3new/Yansongda/Pay/Event/PayEnd.php
new file mode 100644
index 0000000..682a2c6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Event/PayEnd.php
@@ -0,0 +1,9 @@
+plugins = $plugins;
+ $this->params = $params;
+
+ parent::__construct($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Exception/DecryptException.php b/server/addons/epay/library/v3new/Yansongda/Pay/Exception/DecryptException.php
new file mode 100644
index 0000000..f2a869d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Exception/DecryptException.php
@@ -0,0 +1,19 @@
+extra = $extra;
+
+ parent::__construct($message, $code, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Exception/InvalidSignException.php b/server/addons/epay/library/v3new/Yansongda/Pay/Exception/InvalidSignException.php
new file mode 100644
index 0000000..d75b7ca
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Exception/InvalidSignException.php
@@ -0,0 +1,23 @@
+callback = $extra;
+
+ parent::__construct($message, $code, $extra, $previous);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Functions.php b/server/addons/epay/library/v3new/Yansongda/Pay/Functions.php
new file mode 100644
index 0000000..b9a435d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Functions.php
@@ -0,0 +1,656 @@
+ $payload?->get('_service_url') ?? $payload?->get('_url') ?? null,
+ Pay::MODE_SANDBOX => $payload?->get('_sandbox_url') ?? $payload?->get('_url') ?? null,
+ default => $payload?->get('_url') ?? null,
+ };
+}
+
+/**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+function get_provider_config(string $provider, array $params = []): array
+{
+ /** @var ConfigInterface $config */
+ $config = Pay::get(ConfigInterface::class);
+
+ return $config->get($provider, [])[get_tenant($params)] ?? [];
+}
+
+/**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+#[Deprecated(reason: '自 v3.7.5 开始废弃', replacement: 'get_provider_config')]
+function get_alipay_config(array $params = []): array
+{
+ $alipay = Pay::get(ConfigInterface::class)->get('alipay');
+
+ return $alipay[get_tenant($params)] ?? [];
+}
+
+function get_alipay_url(array $config, ?Collection $payload): string
+{
+ $url = get_radar_url($config, $payload) ?? '';
+
+ if (str_starts_with($url, 'http')) {
+ return $url;
+ }
+
+ return Alipay::URL[$config['mode'] ?? Pay::MODE_NORMAL];
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+function verify_alipay_sign(array $config, string $contents, string $sign): void
+{
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 验证支付宝签名失败-支付宝签名为空', func_get_args());
+ }
+
+ $public = $config['alipay_public_cert_path'] ?? null;
+
+ if (empty($public)) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [alipay_public_cert_path]');
+ }
+
+ $result = 1 === openssl_verify(
+ $contents,
+ base64_decode($sign),
+ get_public_cert($public),
+ OPENSSL_ALGO_SHA256
+ );
+
+ if (!$result) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证支付宝签名失败', func_get_args());
+ }
+}
+
+/**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+#[Deprecated(reason: '自 v3.7.5 开始废弃', replacement: 'get_provider_config')]
+function get_wechat_config(array $params = []): array
+{
+ $wechat = Pay::get(ConfigInterface::class)->get('wechat');
+
+ return $wechat[get_tenant($params)] ?? [];
+}
+
+function get_wechat_method(?Collection $payload): string
+{
+ return get_radar_method($payload) ?? 'POST';
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_wechat_url(array $config, ?Collection $payload): string
+{
+ $url = get_radar_url($config, $payload);
+
+ if (empty($url)) {
+ throw new InvalidParamsException(Exception::PARAMS_WECHAT_URL_MISSING, '参数异常: 微信 `_url` 或 `_service_url` 参数缺失:你可能用错插件顺序,应该先使用 `业务插件`');
+ }
+
+ if (str_starts_with($url, 'http')) {
+ return $url;
+ }
+
+ return Wechat::URL[$config['mode'] ?? Pay::MODE_NORMAL].$url;
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_wechat_body(?Collection $payload): mixed
+{
+ $body = get_radar_body($payload);
+
+ if (is_null($body)) {
+ throw new InvalidParamsException(Exception::PARAMS_WECHAT_BODY_MISSING, '参数异常: 微信 `_body` 参数缺失:你可能用错插件顺序,应该先使用 `AddPayloadBodyPlugin`');
+ }
+
+ return $body;
+}
+
+function get_wechat_type_key(array $params): string
+{
+ $key = ($params['_type'] ?? 'mp').'_app_id';
+
+ if ('app_app_id' === $key) {
+ $key = 'app_id';
+ }
+
+ return $key;
+}
+
+/**
+ * @throws InvalidConfigException
+ */
+function get_wechat_sign(array $config, string $contents): string
+{
+ $privateKey = $config['mch_secret_cert'] ?? null;
+
+ if (empty($privateKey)) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_cert]');
+ }
+
+ $privateKey = get_private_cert($privateKey);
+
+ openssl_sign($contents, $sign, $privateKey, 'sha256WithRSAEncryption');
+
+ return base64_encode($sign);
+}
+
+/**
+ * @throws InvalidConfigException
+ */
+function get_wechat_sign_v2(array $config, array $payload, bool $upper = true): string
+{
+ $key = $config['mch_secret_key_v2'] ?? null;
+
+ if (empty($key)) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_key_v2]');
+ }
+
+ ksort($payload);
+
+ $buff = '';
+
+ foreach ($payload as $k => $v) {
+ $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+ }
+
+ $sign = md5($buff.'key='.$key);
+
+ return $upper ? strtoupper($sign) : $sign;
+}
+
+/**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws InvalidSignException
+ * @throws ServiceNotFoundException
+ */
+function verify_wechat_sign(ResponseInterface|ServerRequestInterface $message, array $params): void
+{
+ if ($message instanceof ServerRequestInterface && 'localhost' === $message->getUri()->getHost()) {
+ return;
+ }
+
+ $wechatSerial = $message->getHeaderLine('Wechatpay-Serial');
+ $timestamp = $message->getHeaderLine('Wechatpay-Timestamp');
+ $random = $message->getHeaderLine('Wechatpay-Nonce');
+ $sign = $message->getHeaderLine('Wechatpay-Signature');
+ $body = (string) $message->getBody();
+
+ $content = $timestamp."\n".$random."\n".$body."\n";
+ $public = get_provider_config('wechat', $params)['wechat_public_cert_path'][$wechatSerial] ?? null;
+
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 微信签名为空', ['headers' => $message->getHeaders(), 'body' => $body]);
+ }
+
+ $public = get_public_cert(
+ empty($public) ? reload_wechat_public_certs($params, $wechatSerial) : $public
+ );
+
+ $result = 1 === openssl_verify(
+ $content,
+ base64_decode($sign),
+ $public,
+ 'sha256WithRSAEncryption'
+ );
+
+ if (!$result) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证微信签名失败', ['headers' => $message->getHeaders(), 'body' => $body]);
+ }
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+function verify_wechat_sign_v2(array $config, array $destination): void
+{
+ $sign = $destination['sign'] ?? null;
+
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 微信签名为空', $destination);
+ }
+
+ $key = $config['mch_secret_key_v2'] ?? null;
+
+ if (empty($key)) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_key_v2]');
+ }
+
+ if (get_wechat_sign_v2($config, $destination) !== $sign) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证微信签名失败', $destination);
+ }
+}
+
+function encrypt_wechat_contents(string $contents, string $publicKey): ?string
+{
+ if (openssl_public_encrypt($contents, $encrypted, get_public_cert($publicKey), OPENSSL_PKCS1_OAEP_PADDING)) {
+ return base64_encode($encrypted);
+ }
+
+ return null;
+}
+
+function decrypt_wechat_contents(string $encrypted, array $config): ?string
+{
+ if (openssl_private_decrypt(base64_decode($encrypted), $decrypted, get_private_cert($config['mch_secret_cert'] ?? ''), OPENSSL_PKCS1_OAEP_PADDING)) {
+ return $decrypted;
+ }
+
+ return null;
+}
+
+/**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+function reload_wechat_public_certs(array $params, ?string $serialNo = null): string
+{
+ $data = Pay::wechat()->pay(
+ [StartPlugin::class, WechatPublicCertsPlugin::class, AddPayloadBodyPlugin::class, AddPayloadSignaturePlugin::class, AddRadarPlugin::class, ResponsePlugin::class, ParserPlugin::class],
+ $params
+ )->get('data', []);
+
+ $wechatConfig = get_provider_config('wechat', $params);
+
+ foreach ($data as $item) {
+ $certs[$item['serial_no']] = decrypt_wechat_resource($item['encrypt_certificate'], $wechatConfig)['ciphertext'] ?? '';
+ }
+
+ Pay::get(ConfigInterface::class)->set(
+ 'wechat.'.get_tenant($params).'.wechat_public_cert_path',
+ ((array) ($wechatConfig['wechat_public_cert_path'] ?? [])) + ($certs ?? []),
+ );
+
+ if (!is_null($serialNo) && empty($certs[$serialNo])) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 获取微信 wechat_public_cert_path 配置失败');
+ }
+
+ return $certs[$serialNo] ?? '';
+}
+
+/**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+function get_wechat_public_certs(array $params = [], ?string $path = null): void
+{
+ reload_wechat_public_certs($params);
+
+ $config = get_provider_config('wechat', $params);
+
+ if (empty($path)) {
+ var_dump($config['wechat_public_cert_path']);
+
+ return;
+ }
+
+ foreach ($config['wechat_public_cert_path'] as $serialNo => $cert) {
+ file_put_contents($path.'/'.$serialNo.'.crt', $cert);
+ }
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws DecryptException
+ */
+function decrypt_wechat_resource(array $resource, array $config): array
+{
+ $ciphertext = base64_decode($resource['ciphertext'] ?? '');
+ $secret = $config['mch_secret_key'] ?? null;
+
+ if (strlen($ciphertext) <= Wechat::AUTH_TAG_LENGTH_BYTE) {
+ throw new DecryptException(Exception::DECRYPT_WECHAT_CIPHERTEXT_PARAMS_INVALID, '加解密异常: ciphertext 位数过短');
+ }
+
+ if (is_null($secret) || Wechat::MCH_SECRET_KEY_LENGTH_BYTE != strlen($secret)) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_key]');
+ }
+
+ $resource['ciphertext'] = match ($resource['algorithm'] ?? '') {
+ 'AEAD_AES_256_GCM' => decrypt_wechat_resource_aes_256_gcm($ciphertext, $secret, $resource['nonce'] ?? '', $resource['associated_data'] ?? ''),
+ default => throw new DecryptException(Exception::DECRYPT_WECHAT_DECRYPTED_METHOD_INVALID, '加解密异常: algorithm 不支持'),
+ };
+
+ return $resource;
+}
+
+/**
+ * @throws DecryptException
+ */
+function decrypt_wechat_resource_aes_256_gcm(string $ciphertext, string $secret, string $nonce, string $associatedData): array|string
+{
+ $decrypted = openssl_decrypt(
+ substr($ciphertext, 0, -Wechat::AUTH_TAG_LENGTH_BYTE),
+ 'aes-256-gcm',
+ $secret,
+ OPENSSL_RAW_DATA,
+ $nonce,
+ substr($ciphertext, -Wechat::AUTH_TAG_LENGTH_BYTE),
+ $associatedData
+ );
+
+ if (false === $decrypted) {
+ throw new DecryptException(Exception::DECRYPT_WECHAT_ENCRYPTED_DATA_INVALID, '加解密异常: 解密失败,请检查微信 mch_secret_key 是否正确');
+ }
+
+ if ('certificate' !== $associatedData) {
+ $decrypted = json_decode($decrypted, true);
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new DecryptException(Exception::DECRYPT_WECHAT_ENCRYPTED_DATA_INVALID, '加解密异常: 待解密数据非正常数据');
+ }
+ }
+
+ return $decrypted;
+}
+
+/**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+function get_wechat_serial_no(array $params): string
+{
+ if (!empty($params['_serial_no'])) {
+ return $params['_serial_no'];
+ }
+
+ $config = get_provider_config('wechat', $params);
+
+ if (empty($config['wechat_public_cert_path'])) {
+ reload_wechat_public_certs($params);
+
+ $config = get_provider_config('wechat', $params);
+ }
+
+ mt_srand();
+
+ return strval(array_rand($config['wechat_public_cert_path']));
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_wechat_public_key(array $config, string $serialNo): string
+{
+ $publicKey = $config['wechat_public_cert_path'][$serialNo] ?? null;
+
+ if (empty($publicKey)) {
+ throw new InvalidParamsException(Exception::PARAMS_WECHAT_SERIAL_NOT_FOUND, '参数异常: 微信公钥序列号未找到 - '.$serialNo);
+ }
+
+ return $publicKey;
+}
+
+/**
+ * @throws InvalidConfigException
+ */
+function get_wechat_miniprogram_pay_sign(array $config, string $url, string $payload): string
+{
+ if (empty($config['mini_app_key_virtual_pay'])) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mini_app_key_virtual_pay]');
+ }
+
+ return hash_hmac('sha256', $url.'&'.$payload, $config['mini_app_key_virtual_pay']);
+}
+
+function get_wechat_miniprogram_user_sign(string $sessionKey, string $payload): string
+{
+ return hash_hmac('sha256', $payload, $sessionKey);
+}
+
+/**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+#[Deprecated(reason: '自 v3.7.5 开始废弃', replacement: 'get_provider_config')]
+function get_unipay_config(array $params = []): array
+{
+ $unipay = Pay::get(ConfigInterface::class)->get('unipay');
+
+ return $unipay[get_tenant($params)] ?? [];
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+function verify_unipay_sign(array $config, string $contents, string $sign, ?string $signPublicKeyCert = null): void
+{
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 银联签名为空', func_get_args());
+ }
+
+ if (empty($signPublicKeyCert) && empty($public = $config['unipay_public_cert_path'] ?? null)) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 缺少银联配置 -- [unipay_public_cert_path]');
+ }
+
+ $result = 1 === openssl_verify(
+ hash('sha256', $contents),
+ base64_decode($sign),
+ get_public_cert($signPublicKeyCert ?? $public ?? ''),
+ 'sha256'
+ );
+
+ if (!$result) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证银联签名失败', func_get_args());
+ }
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_unipay_url(array $config, ?Collection $payload): string
+{
+ $url = get_radar_url($config, $payload);
+
+ if (empty($url)) {
+ throw new InvalidParamsException(Exception::PARAMS_UNIPAY_URL_MISSING, '参数异常: 银联 `_url` 参数缺失:你可能用错插件顺序,应该先使用 `业务插件`');
+ }
+
+ if (str_starts_with($url, 'http')) {
+ return $url;
+ }
+
+ return Unipay::URL[$config['mode'] ?? Pay::MODE_NORMAL].$url;
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_unipay_body(?Collection $payload): string
+{
+ $body = get_radar_body($payload);
+
+ if (is_null($body)) {
+ throw new InvalidParamsException(Exception::PARAMS_UNIPAY_BODY_MISSING, '参数异常: 银联 `_body` 参数缺失:你可能用错插件顺序,应该先使用 `AddPayloadBodyPlugin`');
+ }
+
+ return $body;
+}
+
+/**
+ * @throws InvalidConfigException
+ */
+function get_unipay_sign_qra(array $config, array $payload): string
+{
+ $key = $config['mch_secret_key'] ?? null;
+
+ if (empty($key)) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 缺少银联配置 -- [mch_secret_key]');
+ }
+
+ ksort($payload);
+
+ $buff = '';
+
+ foreach ($payload as $k => $v) {
+ $buff .= ('sign' != $k && '' != $v && !is_array($v)) ? $k.'='.$v.'&' : '';
+ }
+
+ return strtoupper(md5($buff.'key='.$key));
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+function verify_unipay_sign_qra(array $config, array $destination): void
+{
+ $sign = $destination['sign'] ?? null;
+
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 银联签名为空', $destination);
+ }
+
+ $key = $config['mch_secret_key'] ?? null;
+
+ if (empty($key)) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 缺少银联配置 -- [mch_secret_key]');
+ }
+
+ if (get_unipay_sign_qra($config, $destination) !== $sign) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证银联签名失败', $destination);
+ }
+}
+
+function get_jsb_url(array $config, ?Collection $payload): string
+{
+ $url = get_radar_url($config, $payload) ?? '';
+
+ if (str_starts_with($url, 'http')) {
+ return $url;
+ }
+
+ return Jsb::URL[$config['mode'] ?? Pay::MODE_NORMAL];
+}
+
+/**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+function verify_jsb_sign(array $config, string $content, string $sign): void
+{
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 江苏银行签名为空', func_get_args());
+ }
+
+ $publicCert = $config['jsb_public_cert_path'] ?? null;
+
+ if (empty($publicCert)) {
+ throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [jsb_public_cert_path]');
+ }
+
+ $result = 1 === openssl_verify(
+ $content,
+ base64_decode($sign),
+ get_public_cert($publicCert)
+ );
+
+ if (!$result) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证江苏银行签名失败', func_get_args());
+ }
+}
+
+/**
+ * @throws InvalidParamsException
+ */
+function get_douyin_url(array $config, ?Collection $payload): string
+{
+ $url = get_radar_url($config, $payload);
+
+ if (empty($url)) {
+ throw new InvalidParamsException(Exception::PARAMS_DOUYIN_URL_MISSING, '参数异常: 抖音 `_url` 参数缺失:你可能用错插件顺序,应该先使用 `业务插件`');
+ }
+
+ if (str_starts_with($url, 'http')) {
+ return $url;
+ }
+
+ return Douyin::URL[$config['mode'] ?? Pay::MODE_NORMAL].$url;
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Pay.php b/server/addons/epay/library/v3new/Yansongda/Pay/Pay.php
new file mode 100644
index 0000000..d10beba
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Pay.php
@@ -0,0 +1,108 @@
+ $rocket]);
+
+ $rocket->mergePayload(['sign' => $this->getSign($rocket)]);
+
+ Logger::info('[Alipay][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getSign(Rocket $rocket): string
+ {
+ $privateKey = $this->getPrivateKey($rocket->getParams());
+
+ $content = $rocket->getPayload()->sortKeys()->toString();
+
+ openssl_sign($content, $sign, $privateKey, OPENSSL_ALGO_SHA256);
+
+ return base64_encode($sign);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getPrivateKey(array $params): string
+ {
+ $privateKey = get_provider_config('alipay', $params)['app_secret_cert'] ?? null;
+
+ if (is_null($privateKey)) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [app_secret_cert]');
+ }
+
+ return get_private_cert($privateKey);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AddRadarPlugin.php
new file mode 100644
index 0000000..cc99de1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AddRadarPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('alipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setRadar(new Request(
+ // 这里因为支付宝的 payload 里不包含 _method,所以需要取 params 中的
+ get_radar_method(new Collection($params)) ?? 'POST',
+ get_alipay_url($config, $payload),
+ $this->getHeaders(),
+ // 不能用 packer,支付宝接收的是 x-www-form-urlencoded 返回的又是 json,packer 用的是返回.
+ $payload?->query() ?? '',
+ ));
+
+ Logger::info('[Alipay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'Content-Type' => 'application/x-www-form-urlencoded',
+ 'User-Agent' => 'yansongda/pay-v3',
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AppCallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AppCallbackPlugin.php
new file mode 100644
index 0000000..ab9129a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/AppCallbackPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('alipay', $params);
+
+ if (empty($params['alipay_trade_app_pay_response'])) {
+ throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID);
+ }
+
+ $value = filter_params($params['alipay_trade_app_pay_response']);
+
+ verify_alipay_sign($config, $value->toJson(), $params['sign'] ?? '');
+
+ $rocket->setPayload($params)
+ ->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload());
+
+ Logger::info('[Alipay][AppCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/CallbackPlugin.php
new file mode 100644
index 0000000..cb6c850
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/CallbackPlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('alipay', $params);
+
+ $value = filter_params($params, fn ($k, $v) => '' !== $v && 'sign' != $k && 'sign_type' != $k);
+
+ verify_alipay_sign($config, $value->sortKeys()->toString(), $params['sign'] ?? '');
+
+ $rocket->setPayload($params)
+ ->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload());
+
+ Logger::info('[Alipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/FormatPayloadBizContentPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/FormatPayloadBizContentPlugin.php
new file mode 100644
index 0000000..4c637aa
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/FormatPayloadBizContentPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ $rocket->setPayload(
+ filter_params($payload->all(), fn ($k, $v) => '' !== $v && 'sign' != $k)
+ ->merge([
+ 'biz_content' => json_encode(filter_params($payload->get('biz_content', []))),
+ ])
+ );
+
+ Logger::info('[Alipay][FormatPayloadBizContentPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/AppPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/AppPayPlugin.php
new file mode 100644
index 0000000..9fbbc62
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/AppPayPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.app.pay',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][PCreditPayInstallment][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/H5PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/H5PayPlugin.php
new file mode 100644
index 0000000..d468db4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/H5PayPlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.wap.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'QUICK_WAP_WAY',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Fund][PCreditPayInstallment][H5PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/PosPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/PosPayPlugin.php
new file mode 100644
index 0000000..e18f134
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/PosPayPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'FACE_TO_FACE_PAYMENT',
+ 'scene' => 'bar_code',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Fund][PCreditPayInstallment][PosPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/ScanPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/ScanPayPlugin.php
new file mode 100644
index 0000000..35c7d11
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/PCreditPayInstallment/ScanPayPlugin.php
@@ -0,0 +1,41 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.precreate',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][PCreditPayInstallment][ScanPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/OnsettlePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/OnsettlePlugin.php
new file mode 100644
index 0000000..2926941
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/OnsettlePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.order.onsettle.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Query][OnsettlePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/RatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/RatePlugin.php
new file mode 100644
index 0000000..aeea5e8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/RatePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.royalty.rate.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Query][RatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/SettlePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/SettlePlugin.php
new file mode 100644
index 0000000..94c8c7e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Query/SettlePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.order.settle.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Query][SettlePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/BindPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/BindPlugin.php
new file mode 100644
index 0000000..2498da6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/BindPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.royalty.relation.bind',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Relation][BindPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/QueryPlugin.php
new file mode 100644
index 0000000..3af441e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.royalty.relation.batchquery',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Relation][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/UnbindPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/UnbindPlugin.php
new file mode 100644
index 0000000..376fc62
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Relation/UnbindPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.royalty.relation.unbind',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Relation][UnbindPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Request/SettleOrderPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Request/SettleOrderPlugin.php
new file mode 100644
index 0000000..d01b340
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Royalty/Request/SettleOrderPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.order.settle',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Royalty][Request][SettleOrderPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/ApplyReceiptPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/ApplyReceiptPlugin.php
new file mode 100644
index 0000000..83a650f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/ApplyReceiptPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.bill.ereceipt.apply',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][ApplyReceiptPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Bill/QueryUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Bill/QueryUrlPlugin.php
new file mode 100644
index 0000000..8cb1edd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Bill/QueryUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][Bill][QueryUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/QueryPlugin.php
new file mode 100644
index 0000000..c030417
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.trans.common.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][Fund][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/TransferPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/TransferPlugin.php
new file mode 100644
index 0000000..d7a4033
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/Fund/TransferPlugin.php
@@ -0,0 +1,37 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.trans.uni.transfer',
+ 'biz_content' => array_merge(
+ [
+ 'biz_scene' => 'DIRECT_TRANSFER',
+ 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][Fund][TransferPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryAccountPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryAccountPlugin.php
new file mode 100644
index 0000000..4297e88
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryAccountPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.account.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][QueryAccountPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryReceiptPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryReceiptPlugin.php
new file mode 100644
index 0000000..5efdcfd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/QueryReceiptPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.bill.ereceipt.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][QueryReceiptPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/RefundPlugin.php
new file mode 100644
index 0000000..f7e41ea
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Fund/Transfer/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.trans.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Fund][Transfer][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/AppPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/AppPayPlugin.php
new file mode 100644
index 0000000..2df8f13
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/AppPayPlugin.php
@@ -0,0 +1,38 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.fund.trans.app.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'STD_RED_PACKET',
+ 'biz_scene' => 'PERSONAL_PAY',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Marketing][Redpack][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/WebPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/WebPayPlugin.php
new file mode 100644
index 0000000..4c38b18
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Marketing/Redpack/WebPayPlugin.php
@@ -0,0 +1,37 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.fund.trans.page.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'STD_APP_TRANSFER',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Marketing][Redpack][WebPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/AuthPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/AuthPlugin.php
new file mode 100644
index 0000000..4d0dbf9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/AuthPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.info.auth',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Authorization][AuthPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/QueryPlugin.php
new file mode 100644
index 0000000..5d4f40f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.open.auth.userauth.relationship.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Authorization][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/TokenPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/TokenPlugin.php
new file mode 100644
index 0000000..a267e80
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Authorization/TokenPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->mergePayload(array_merge(
+ [
+ 'method' => 'alipay.system.oauth.token',
+ ],
+ $rocket->getParams(),
+ ));
+
+ Logger::info('[Alipay][Member][Authorization][TokenPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/CertifyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/CertifyPlugin.php
new file mode 100644
index 0000000..d5e8bb4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/CertifyPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.certify.open.certify',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Certification][CertifyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/InitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/InitPlugin.php
new file mode 100644
index 0000000..4e97cab
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/InitPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.certify.open.initialize',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'FACE',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][Certification][AppInitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/QueryPlugin.php
new file mode 100644
index 0000000..b43fdd9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Certification/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.certify.open.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Certification][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/DetailPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/DetailPlugin.php
new file mode 100644
index 0000000..ab71f87
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/DetailPlugin.php
@@ -0,0 +1,33 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+
+ $rocket->mergePayload([
+ 'auth_token' => $params['auth_token'] ?? $params['_auth_token'] ?? '',
+ 'method' => 'alipay.user.info.share',
+ 'biz_content' => [],
+ ]);
+
+ Logger::info('[Alipay][Member][DetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppInitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppInitPlugin.php
new file mode 100644
index 0000000..f3242bb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppInitPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.check.initialize',
+ 'biz_content' => array_merge(
+ [
+ 'biz_code' => 'DATA_DIGITAL_BIZ_CODE_FACE_CHECK_LIVE',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceCheck][AppInitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppQueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppQueryPlugin.php
new file mode 100644
index 0000000..81ce033
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceCheck/AppQueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.check.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceCheck][AppQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppInitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppInitPlugin.php
new file mode 100644
index 0000000..5eb4eb8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppInitPlugin.php
@@ -0,0 +1,36 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.verification.initialize',
+ 'biz_content' => array_merge(
+ [
+ 'biz_code' => 'DATA_DIGITAL_BIZ_CODE_FACE_VERIFICATION',
+ 'identity_type' => 'CERT_INFO',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][AppInitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppQueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppQueryPlugin.php
new file mode 100644
index 0000000..aec240b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/AppQueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.verification.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][AppQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5InitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5InitPlugin.php
new file mode 100644
index 0000000..3cd5ac2
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5InitPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.certify.initialize',
+ 'biz_content' => array_merge(
+ [
+ 'biz_code' => 'FUTURE_TECH_BIZ_FACE_SDK',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][H5InitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5QueryPlugin.php
new file mode 100644
index 0000000..7dda061
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.certify.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][H5QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5VerifyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5VerifyPlugin.php
new file mode 100644
index 0000000..572c8b3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/H5VerifyPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.certify.verify',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][H5VerifyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/ServerVerifyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/ServerVerifyPlugin.php
new file mode 100644
index 0000000..358294f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/FaceVerification/ServerVerifyPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.face.source.certify',
+ 'biz_content' => array_merge(
+ [
+ 'cert_type' => 'IDENTITY_CARD',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][FaceVerification][ServerVerifyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/AppInitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/AppInitPlugin.php
new file mode 100644
index 0000000..88a55e0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/AppInitPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.ocr.mobile.initialize',
+ 'biz_content' => array_merge(
+ [
+ 'biz_code' => 'DATA_DIGITAL_BIZ_CODE_OCR',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Member][Ocr][AppInitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/DetectPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/DetectPlugin.php
new file mode 100644
index 0000000..d8e1d92
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/DetectPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.ocr.common.detect',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Ocr][DetectPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/ServerDetectPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/ServerDetectPlugin.php
new file mode 100644
index 0000000..eaa5518
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Member/Ocr/ServerDetectPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'datadigital.fincloud.generalsaas.ocr.server.detect',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Member][Ocr][ServerDetectPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Bill/QueryUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Bill/QueryUrlPlugin.php
new file mode 100644
index 0000000..0bbe513
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Bill/QueryUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Bill][QueryUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/AppPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/AppPayPlugin.php
new file mode 100644
index 0000000..87807cd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/AppPayPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.app.pay',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/CancelPlugin.php
new file mode 100644
index 0000000..1ce0c4c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/CancelPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.cancel',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/ClosePlugin.php
new file mode 100644
index 0000000..f609d43
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/PayPlugin.php
new file mode 100644
index 0000000..d15a01b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/PayPlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'GENERAL_WITHHOLDING',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/QueryPlugin.php
new file mode 100644
index 0000000..a4d6dbf
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/RefundPlugin.php
new file mode 100644
index 0000000..6e1d259
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Pay/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Pay][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/ModifyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/ModifyPlugin.php
new file mode 100644
index 0000000..a86adf9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/ModifyPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.executionplan.modify',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Sign][ModifyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/QueryPlugin.php
new file mode 100644
index 0000000..7d3dedb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Sign][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/SignPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/SignPlugin.php
new file mode 100644
index 0000000..25eb408
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/SignPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.user.agreement.page.sign',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Sign][SignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/UnsignPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/UnsignPlugin.php
new file mode 100644
index 0000000..d5fae2e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Agreement/Sign/UnsignPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.user.agreement.unsign',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Agreement][Sign][UnsignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/ClosePlugin.php
new file mode 100644
index 0000000..c5cc0c9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/PayPlugin.php
new file mode 100644
index 0000000..4438aa7
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/PayPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.app.pay',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..de0761e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryPlugin.php
new file mode 100644
index 0000000..9370dc9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryRefundPlugin.php
new file mode 100644
index 0000000..4d0bbf6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][QueryRefundPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/RefundPlugin.php
new file mode 100644
index 0000000..f4e658b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/App/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][App][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/AppFreezePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/AppFreezePlugin.php
new file mode 100644
index 0000000..b2585b5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/AppFreezePlugin.php
@@ -0,0 +1,37 @@
+ $rocket]);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.fund.auth.order.app.freeze',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'PREAUTH_PAY',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][AppFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/CancelPlugin.php
new file mode 100644
index 0000000..0470b73
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/CancelPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.operation.cancel',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/PosFreezePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/PosFreezePlugin.php
new file mode 100644
index 0000000..9f5aa38
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/PosFreezePlugin.php
@@ -0,0 +1,36 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.order.freeze',
+ 'biz_content' => array_merge(
+ [
+ 'auth_code_type' => 'bar_code',
+ 'product_code' => 'PREAUTH_PAY',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][PosFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/QueryPlugin.php
new file mode 100644
index 0000000..1a496ef
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.operation.detail.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/ScanFreezePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/ScanFreezePlugin.php
new file mode 100644
index 0000000..113e3b0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/ScanFreezePlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.order.voucher.create',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'PREAUTH_PAY',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][ScanFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/UnfreezePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/UnfreezePlugin.php
new file mode 100644
index 0000000..d77e846
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Auth/UnfreezePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.fund.auth.order.unfreeze',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Auth][AppFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Bill/QueryUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Bill/QueryUrlPlugin.php
new file mode 100644
index 0000000..6f20870
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Bill/QueryUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Bill][QueryUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/ClosePlugin.php
new file mode 100644
index 0000000..70a2fa7
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/PayPlugin.php
new file mode 100644
index 0000000..fdf77bf
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/PayPlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'PREAUTH_PAY',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryPlugin.php
new file mode 100644
index 0000000..b0070f8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryRefundPlugin.php
new file mode 100644
index 0000000..1878430
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][QueryRefundPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/RefundPlugin.php
new file mode 100644
index 0000000..730d360
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/SyncPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/SyncPlugin.php
new file mode 100644
index 0000000..8c205b6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Authorization/Pay/SyncPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.orderinfo.sync',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Authorization][Pay][SyncPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/InitPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/InitPlugin.php
new file mode 100644
index 0000000..2c6057a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/InitPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'zoloz.authentication.smilepay.initialize',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Face][InitPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/QueryPlugin.php
new file mode 100644
index 0000000..5fff49b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Face/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'zoloz.authentication.customer.ftoken.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Face][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/ClosePlugin.php
new file mode 100644
index 0000000..6a95844
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/PayPlugin.php
new file mode 100644
index 0000000..3fdcb15
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/PayPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.wap.pay',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..8377b72
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryPlugin.php
new file mode 100644
index 0000000..0032459
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryRefundPlugin.php
new file mode 100644
index 0000000..1e42b56
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][QueryRefundPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/RefundPlugin.php
new file mode 100644
index 0000000..066bb6e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/H5/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][H5][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/CancelPlugin.php
new file mode 100644
index 0000000..68a0496
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/CancelPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.cancel',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/ClosePlugin.php
new file mode 100644
index 0000000..86d3955
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/PayPlugin.php
new file mode 100644
index 0000000..407e776
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/PayPlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.create',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'JSAPI_PAY',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..e8dd2ea
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryPlugin.php
new file mode 100644
index 0000000..61c0c0f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryRefundPlugin.php
new file mode 100644
index 0000000..8f690c0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/RefundPlugin.php
new file mode 100644
index 0000000..c927370
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Mini/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Mini][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/CancelPlugin.php
new file mode 100644
index 0000000..4324d12
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/CancelPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.cancel',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/ClosePlugin.php
new file mode 100644
index 0000000..1bb49c3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/PayPlugin.php
new file mode 100644
index 0000000..6fdfa80
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/PayPlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.pay',
+ 'biz_content' => array_merge(
+ [
+ 'scene' => 'bar_code',
+ ],
+ $rocket->getParams(),
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..6857612
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryPlugin.php
new file mode 100644
index 0000000..0e54469
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryRefundPlugin.php
new file mode 100644
index 0000000..66844dd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/RefundPlugin.php
new file mode 100644
index 0000000..b245616
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Pos/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Pos][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CancelPlugin.php
new file mode 100644
index 0000000..b8cf716
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CancelPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.cancel',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/ClosePlugin.php
new file mode 100644
index 0000000..a35a0e5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CreatePlugin.php
new file mode 100644
index 0000000..c74b41d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/CreatePlugin.php
@@ -0,0 +1,41 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.create',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/PayPlugin.php
new file mode 100644
index 0000000..4ceecda
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/PayPlugin.php
@@ -0,0 +1,41 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.precreate',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..7700491
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryPlugin.php
new file mode 100644
index 0000000..95b498e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryRefundPlugin.php
new file mode 100644
index 0000000..95c3ad2
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][QueryRefundPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/RefundPlugin.php
new file mode 100644
index 0000000..79c145f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Scan/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/ClosePlugin.php
new file mode 100644
index 0000000..22cd791
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/ClosePlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.close',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/PayPlugin.php
new file mode 100644
index 0000000..6de7c2e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/PayPlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ $this->loadAlipayServiceProvider($rocket);
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ 'method' => 'alipay.trade.page.pay',
+ 'biz_content' => array_merge(
+ [
+ 'product_code' => 'FAST_INSTANT_TRADE_PAY',
+ ],
+ $rocket->getParams()
+ ),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryBillUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryBillUrlPlugin.php
new file mode 100644
index 0000000..a61b972
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryBillUrlPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.data.dataservice.bill.downloadurl.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][QueryBillUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryPlugin.php
new file mode 100644
index 0000000..a4f9237
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryRefundPlugin.php
new file mode 100644
index 0000000..c0f319f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/QueryRefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.fastpay.refund.query',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][QueryRefundPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/RefundPlugin.php
new file mode 100644
index 0000000..70fb333
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/Pay/Web/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'method' => 'alipay.trade.refund',
+ 'biz_content' => $rocket->getParams(),
+ ]);
+
+ Logger::info('[Alipay][Pay][Web][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseHtmlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseHtmlPlugin.php
new file mode 100644
index 0000000..ad6af68
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseHtmlPlugin.php
@@ -0,0 +1,71 @@
+ $rocket]);
+
+ $radar = $rocket->getRadar();
+
+ $response = 'GET' === $radar->getMethod()
+ ? $this->buildRedirect($radar->getUri()->__toString(), $rocket->getPayload())
+ : $this->buildHtml($radar->getUri()->__toString(), $rocket->getPayload());
+
+ $rocket->setDestination($response);
+
+ Logger::info('[Alipay][ResponseHtmlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function buildRedirect(string $endpoint, Collection $payload): Response
+ {
+ $url = $endpoint.(!str_contains($endpoint, '?') ? '?' : '&').$payload->query();
+
+ $content = sprintf(
+ '
+
+
+
+
+
+ Redirecting to %1$s
+
+
+ Redirecting to %1$s.
+
+ ',
+ htmlspecialchars($url, ENT_QUOTES)
+ );
+
+ return new Response(302, ['Location' => $url], $content);
+ }
+
+ protected function buildHtml(string $endpoint, Collection $payload): Response
+ {
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response(200, [], $sHtml);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseInvokeStringPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseInvokeStringPlugin.php
new file mode 100644
index 0000000..d08ccf8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponseInvokeStringPlugin.php
@@ -0,0 +1,31 @@
+ $rocket]);
+
+ $response = new Response(200, [], Arr::query($rocket->getPayload()->all()));
+
+ $rocket->setDestination($response);
+
+ Logger::info('[Alipay][ResponseInvokeStringPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponsePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponsePlugin.php
new file mode 100644
index 0000000..36f2870
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/ResponsePlugin.php
@@ -0,0 +1,51 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $payload = $rocket->getPayload();
+ $resultKey = str_replace('.', '_', $payload->get('method')).'_response';
+
+ if (should_do_http_request($rocket->getDirection()) && $destination instanceof Collection) {
+ $sign = $destination->get('sign', '');
+ $response = $destination->get($resultKey, $destination->all());
+
+ if (empty($sign) && '10000' !== ($response['code'] ?? 'null')) {
+ throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, '支付宝网关响应异常: '.($response['sub_msg'] ?? $response['msg'] ?? '未知错误,请查看支付宝原始响应'), $rocket->getDestination());
+ }
+
+ $rocket->setDestination(new Collection(array_merge(
+ ['_sign' => $sign],
+ $response
+ )));
+ }
+
+ Logger::info('[Alipay][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/StartPlugin.php
new file mode 100644
index 0000000..187a762
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/StartPlugin.php
@@ -0,0 +1,210 @@
+ $rocket]);
+
+ $rocket->mergePayload($this->getPayload($rocket->getParams()));
+
+ Logger::info('[Alipay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ * @throws InvalidConfigException
+ */
+ protected function getPayload(array $params): array
+ {
+ $tenant = get_tenant($params);
+ $config = get_provider_config('alipay', $params);
+
+ return [
+ 'app_id' => $config['app_id'] ?? '',
+ 'method' => '',
+ 'format' => 'JSON',
+ 'return_url' => $this->getReturnUrl($params, $config),
+ 'charset' => 'utf-8',
+ 'sign_type' => 'RSA2',
+ 'sign' => '',
+ 'timestamp' => date('Y-m-d H:i:s'),
+ 'version' => '1.0',
+ 'notify_url' => $this->getNotifyUrl($params, $config),
+ 'app_auth_token' => $this->getAppAuthToken($params, $config),
+ 'app_cert_sn' => $this->getAppCertSn($tenant, $config),
+ 'alipay_root_cert_sn' => $this->getAlipayRootCertSn($tenant, $config),
+ 'biz_content' => [],
+ ];
+ }
+
+ protected function getReturnUrl(array $params, array $config): string
+ {
+ if (!empty($params['_return_url'])) {
+ return $params['_return_url'];
+ }
+
+ return $config['return_url'] ?? '';
+ }
+
+ protected function getNotifyUrl(array $params, array $config): string
+ {
+ if (!empty($params['_notify_url'])) {
+ return $params['_notify_url'];
+ }
+
+ return $config['notify_url'] ?? '';
+ }
+
+ protected function getAppAuthToken(array $params, array $config): string
+ {
+ if (!empty($params['_app_auth_token'])) {
+ return $params['_app_auth_token'];
+ }
+
+ return $config['app_auth_token'] ?? '';
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getAppCertSn(string $tenant, array $config): string
+ {
+ if (!empty($config['app_public_cert_sn'])) {
+ return $config['app_public_cert_sn'];
+ }
+
+ $path = $config['app_public_cert_path'] ?? null;
+
+ if (is_null($path)) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [app_public_cert_path]');
+ }
+
+ $ssl = openssl_x509_parse(get_public_cert($path));
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 解析 `app_public_cert_path` 失败');
+ }
+
+ $result = $this->getCertSn($ssl['issuer'] ?? [], $ssl['serialNumber'] ?? '');
+
+ Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.app_public_cert_sn', $result);
+
+ return $result;
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ protected function getAlipayRootCertSn(string $tenant, array $config): string
+ {
+ if (!empty($config['alipay_root_cert_sn'])) {
+ return $config['alipay_root_cert_sn'];
+ }
+
+ $path = $config['alipay_root_cert_path'] ?? null;
+
+ if (is_null($path)) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [alipay_root_cert_path]');
+ }
+
+ $sn = '';
+ $exploded = explode('-----END CERTIFICATE-----', get_public_cert($path));
+
+ foreach ($exploded as $cert) {
+ if (empty(trim($cert))) {
+ continue;
+ }
+
+ $ssl = openssl_x509_parse($cert.'-----END CERTIFICATE-----');
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 解析 `alipay_root_cert` 失败');
+ }
+
+ $detail = $this->formatCert($ssl);
+
+ if ('sha1WithRSAEncryption' == $detail['signatureTypeLN'] || 'sha256WithRSAEncryption' == $detail['signatureTypeLN']) {
+ $sn .= $this->getCertSn($detail['issuer'], $detail['serialNumber']).'_';
+ }
+ }
+
+ $result = substr($sn, 0, -1);
+
+ Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.alipay_root_cert_sn', $result);
+
+ return $result;
+ }
+
+ protected function getCertSn(array $issuer, string $serialNumber): string
+ {
+ return md5($this->array2string(array_reverse($issuer)).$serialNumber);
+ }
+
+ protected function array2string(array $array): string
+ {
+ $string = [];
+
+ foreach ($array as $key => $value) {
+ $string[] = $key.'='.$value;
+ }
+
+ return implode(',', $string);
+ }
+
+ protected function formatCert(array $ssl): array
+ {
+ if (str_starts_with($ssl['serialNumber'] ?? '', '0x')) {
+ $ssl['serialNumber'] = $this->hex2dec($ssl['serialNumberHex'] ?? '');
+ }
+
+ return $ssl;
+ }
+
+ protected function hex2dec(string $hex): string
+ {
+ $dec = '0';
+ $len = strlen($hex);
+
+ for ($i = 1; $i <= $len; ++$i) {
+ $dec = bcadd(
+ $dec,
+ bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i), 0), 0),
+ 0
+ );
+ }
+
+ return $dec;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/VerifySignaturePlugin.php
new file mode 100644
index 0000000..5e667e5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Alipay/V2/VerifySignaturePlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ $destination = $rocket->getDestination();
+
+ if ((!$destination instanceof Collection) || empty($result = $destination->except('_sign')->all())) {
+ throw new InvalidParamsException(Exception::RESPONSE_EMPTY, '参数异常: 支付宝验证签名时待验签参数不正确', $destination);
+ }
+
+ $config = get_provider_config('alipay', $rocket->getParams());
+
+ verify_alipay_sign($config, json_encode($result, JSON_UNESCAPED_UNICODE), $destination->get('_sign', ''));
+
+ Logger::info('[Alipay][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddPayloadSignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddPayloadSignaturePlugin.php
new file mode 100644
index 0000000..7a83f97
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddPayloadSignaturePlugin.php
@@ -0,0 +1,94 @@
+ $rocket]);
+
+ $config = get_provider_config('douyin', $rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ $rocket->mergePayload(['sign' => $this->getSign($config, filter_params($payload))]);
+
+ Logger::info('[Douyin][V1][Pay][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(array $config, Collection $payload): string
+ {
+ $salt = $config['mch_secret_salt'] ?? null;
+
+ if (empty($salt)) {
+ throw new InvalidConfigException(Exception::CONFIG_DOUYIN_INVALID, '配置异常: 缺少抖音配置 -- [mch_secret_salt]');
+ }
+
+ foreach ($payload as $key => $value) {
+ if (is_string($value)) {
+ $value = trim($value);
+ }
+
+ if (in_array($key, ['other_settle_params', 'app_id', 'sign', 'thirdparty_id']) || empty($value) || 'null' === $value) {
+ continue;
+ }
+
+ if (is_array($value)) {
+ $value = $this->arrayToString($value);
+ }
+
+ $signData[] = $value;
+ }
+
+ $signData[] = $salt;
+
+ sort($signData, SORT_STRING);
+
+ return md5(implode('&', $signData));
+ }
+
+ protected function arrayToString(array $value): string
+ {
+ $isJsonArray = isset($value[0]);
+ $keys = array_keys($value);
+
+ if ($isJsonArray) {
+ sort($keys);
+ }
+
+ foreach ($keys as $key) {
+ $val = $value[$key];
+
+ $result[] = is_array($val) ? $this->arrayToString($val) : (($isJsonArray ? '' : $key.':').trim(strval($val)));
+ }
+
+ $result = '['.implode(' ', $result ?? []).']';
+
+ return ($isJsonArray ? '' : 'map').$result;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddRadarPlugin.php
new file mode 100644
index 0000000..b7a020c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/AddRadarPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('douyin', $params);
+
+ $rocket->setRadar(new Request(
+ get_radar_method($payload),
+ get_douyin_url($config, $payload),
+ $this->getHeaders(),
+ get_radar_body($payload),
+ ));
+
+ Logger::info('[Douyin][V1][Pay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'User-Agent' => 'yansongda/pay-v3',
+ 'Content-Type' => 'application/json; charset=utf-8',
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/CallbackPlugin.php
new file mode 100644
index 0000000..c348f48
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/CallbackPlugin.php
@@ -0,0 +1,74 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('douyin', $params);
+
+ $value = filter_params($params, fn ($k, $v) => '' !== $v && 'msg_signature' != $k && 'type' != $k);
+
+ $this->verifySign($config, $value->all(), $params['msg_signature'] ?? '');
+
+ $rocket->setPayload($params)
+ ->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload());
+
+ Logger::info('[Douyin][V1][Pay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws InvalidSignException
+ */
+ protected function verifySign(array $config, array $contents, string $sign): void
+ {
+ if (empty($sign)) {
+ throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 验证抖音签名失败-抖音签名为空', func_get_args());
+ }
+
+ $contents['token'] = $config['mch_secret_token'] ?? null;
+
+ if (empty($contents['token'])) {
+ throw new InvalidConfigException(Exception::CONFIG_DOUYIN_INVALID, '配置异常: 缺少抖音配置 -- [mch_secret_token]');
+ }
+
+ sort($contents, SORT_STRING);
+ $data = trim(implode('', $contents));
+
+ $result = $sign === sha1($data);
+
+ if (!$result) {
+ throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证抖音签名失败', func_get_args());
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/PayPlugin.php
new file mode 100644
index 0000000..3926149
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/PayPlugin.php
@@ -0,0 +1,72 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('douyin', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 抖音小程序下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'api/apps/ecpay/v1/create_order',
+ 'app_id' => $config['mini_app_id'] ?? '',
+ 'notify_url' => $payload->get('notify_url') ?? $this->getNotifyUrl($config),
+ ],
+ $data ?? [],
+ ));
+
+ Logger::info('[Douyin][V1][Pay][Mini][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'thirdparty_id' => $payload->get('thirdparty_id', $config['thirdparty_id'] ?? ''),
+ ];
+ }
+
+ protected function getNotifyUrl(array $config): ?string
+ {
+ return empty($config['notify_url']) ? null : $config['notify_url'];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryPlugin.php
new file mode 100644
index 0000000..3dbca6d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryPlugin.php
@@ -0,0 +1,66 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('douyin', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 抖音小程序查询订单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'api/apps/ecpay/v1/query_order',
+ 'app_id' => $config['mini_app_id'] ?? '',
+ ],
+ $data ?? [],
+ ));
+
+ Logger::info('[Douyin][V1][Pay][Mini][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'thirdparty_id' => $payload->get('thirdparty_id', $config['thirdparty_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryRefundPlugin.php
new file mode 100644
index 0000000..8f15e3a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/QueryRefundPlugin.php
@@ -0,0 +1,66 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('douyin', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 抖音小程序查询退款订单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'api/apps/ecpay/v1/query_refund',
+ 'app_id' => $config['mini_app_id'] ?? '',
+ ],
+ $data ?? [],
+ ));
+
+ Logger::info('[Douyin][V1][Pay][Mini][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'thirdparty_id' => $payload->get('thirdparty_id', $config['thirdparty_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/RefundPlugin.php
new file mode 100644
index 0000000..7186461
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/Mini/RefundPlugin.php
@@ -0,0 +1,72 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('douyin', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 抖音小程序退款订单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'api/apps/ecpay/v1/create_refund',
+ 'app_id' => $config['mini_app_id'] ?? '',
+ 'notify_url' => $payload->get('notify_url') ?? $this->getNotifyUrl($config),
+ ],
+ $data ?? [],
+ ));
+
+ Logger::info('[Douyin][V1][Pay][Mini][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'thirdparty_id' => $payload->get('thirdparty_id', $config['thirdparty_id'] ?? ''),
+ ];
+ }
+
+ protected function getNotifyUrl(array $config): ?string
+ {
+ return empty($config['notify_url']) ? null : $config['notify_url'];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/ResponsePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/ResponsePlugin.php
new file mode 100644
index 0000000..df4939a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Douyin/V1/Pay/ResponsePlugin.php
@@ -0,0 +1,51 @@
+ $rocket]);
+
+ $this->validateResponse($rocket);
+
+ Logger::info('[Douyin][V1][Pay][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidResponseException
+ */
+ protected function validateResponse(Rocket $rocket): void
+ {
+ $destination = $rocket->getDestination();
+ $response = $rocket->getDestinationOrigin();
+
+ if ($response instanceof ResponseInterface
+ && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300)) {
+ throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '抖音返回状态码异常,请检查参数是否错误', $destination);
+ }
+
+ if (0 !== $destination->get('err_no')) {
+ throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, '抖音返回业务异常: '.$destination->get('err_tips'), $destination);
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddPayloadSignPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddPayloadSignPlugin.php
new file mode 100644
index 0000000..a3c0295
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddPayloadSignPlugin.php
@@ -0,0 +1,65 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+ $payload = $rocket->getPayload();
+
+ if (empty($payload) || $payload->isEmpty()) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少支付必要参数。可能插件用错顺序,应该先使用 `业务插件`');
+ }
+
+ $privateCertPath = $config['mch_secret_cert_path'] ?? '';
+
+ if (empty($privateCertPath)) {
+ throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [mch_secret_cert_path]');
+ }
+
+ $rocket->mergePayload([
+ 'signType' => 'RSA',
+ 'sign' => $this->getSignature(get_private_cert($privateCertPath), $payload),
+ ]);
+
+ Logger::info('[Jsb][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getSignature(string $pkey, Collection $payload): string
+ {
+ $content = $payload->sortKeys()->toString();
+
+ openssl_sign($content, $signature, $pkey);
+
+ return base64_encode($signature);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddRadarPlugin.php
new file mode 100644
index 0000000..870382a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/AddRadarPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setRadar(new Request(
+ strtoupper($params['_method'] ?? 'POST'),
+ get_jsb_url($config, $payload),
+ $this->getHeaders(),
+ $this->getBody($payload),
+ ));
+
+ Logger::info('[Jsb][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'Content-Type' => 'text/html',
+ 'User-Agent' => 'yansongda/pay-v3',
+ ];
+ }
+
+ protected function getBody(Collection $payload): string
+ {
+ $sign = $payload->get('sign');
+ $signType = $payload->get('signType');
+
+ $payload->forget('sign');
+ $payload->forget('signType');
+
+ $payload = $payload->sortKeys();
+
+ $payload->set('sign', $sign);
+ $payload->set('signType', $signType);
+
+ return $payload->toString();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/CallbackPlugin.php
new file mode 100644
index 0000000..bb9940d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/CallbackPlugin.php
@@ -0,0 +1,70 @@
+ $rocket]);
+
+ $this->formatRequestAndParams($rocket);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+
+ $payload = $rocket->getPayload();
+ $signature = $payload->get('sign');
+
+ $payload->forget('sign');
+ $payload->forget('signType');
+
+ verify_jsb_sign($config, $payload->sortKeys()->toString(), $signature);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload());
+
+ Logger::info('[Jsb][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function formatRequestAndParams(Rocket $rocket): void
+ {
+ $request = $rocket->getParams()['request'] ?? null;
+
+ if (!$request instanceof Collection) {
+ throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID);
+ }
+
+ $rocket->setPayload($request)->setParams($rocket->getParams()['params'] ?? []);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/PayPlugin.php
new file mode 100644
index 0000000..dc9392d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/PayPlugin.php
@@ -0,0 +1,49 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+ $backUrl = $rocket->getPayload()['notify_url'] ?? $config['notify_url'] ?? null;
+
+ if (!$backUrl) {
+ throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [notify_url]');
+ }
+
+ $rocket->mergePayload([
+ 'service' => 'atPay',
+ 'backUrl' => $backUrl,
+ ]);
+
+ Logger::info('[Jsb][Pay][Scan][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/QueryPlugin.php
new file mode 100644
index 0000000..47b4e89
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/QueryPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'deviceNo' => '1234567890',
+ 'service' => 'payCheck',
+ ]);
+
+ Logger::info('[Jsb][Pay][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/RefundPlugin.php
new file mode 100644
index 0000000..e770810
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/Pay/Scan/RefundPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ 'deviceNo' => '1234567890',
+ 'service' => 'payRefund',
+ ]);
+
+ Logger::info('[Jsb][Pay][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/ResponsePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/ResponsePlugin.php
new file mode 100644
index 0000000..023ba95
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/ResponsePlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $this->validateResponse($rocket);
+
+ Logger::info('[Jsb][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidResponseException
+ */
+ protected function validateResponse(Rocket $rocket): void
+ {
+ $destination = $rocket->getDestination();
+ $destinationOrigin = $rocket->getDestinationOrigin();
+
+ if ($destinationOrigin instanceof ResponseInterface
+ && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) {
+ throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '江苏银行返回状态码异常,请检查参数是否错误', $rocket->getDestination());
+ }
+
+ if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) {
+ throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, sprintf('江苏银行返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination());
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/StartPlugin.php
new file mode 100644
index 0000000..22a1b12
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/StartPlugin.php
@@ -0,0 +1,49 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload(array_merge($params, [
+ 'createData' => date('Ymd'),
+ 'createTime' => date('His'),
+ 'bizDate' => date('Ymd'),
+ 'msgId' => Str::uuidV4(),
+ 'svrCode' => $config['svr_code'] ?? '',
+ 'partnerId' => $config['partner_id'] ?? '',
+ 'channelNo' => 'm',
+ 'publicKeyCode' => $config['public_key_code'] ?? '',
+ 'version' => 'v1.0.0',
+ 'charset' => 'utf-8',
+ ]));
+
+ Logger::info('[Jsb][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/VerifySignaturePlugin.php
new file mode 100644
index 0000000..635cbf1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Jsb/VerifySignaturePlugin.php
@@ -0,0 +1,76 @@
+ $rocket]);
+
+ if (should_do_http_request($rocket->getDirection())) {
+ $params = $rocket->getParams();
+ $config = get_provider_config('jsb', $params);
+
+ $body = (string) $rocket->getDestinationOrigin()->getBody();
+ $signatureData = $this->getSignatureData($body);
+
+ verify_jsb_sign($config, $signatureData['data'] ?? '', $signatureData['sign'] ?? '');
+ }
+
+ Logger::info('[Jsb][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ private function getSignatureData(string $body): array
+ {
+ if (Str::contains($body, '&-&')) {
+ $beginIndex = strpos($body, '&signType=');
+ $endIndex = strpos($body, '&-&');
+ $data = substr($body, 0, $beginIndex).substr($body, $endIndex);
+
+ $signIndex = strpos($body, '&sign=');
+ $signature = substr($body, $signIndex + strlen('&sign='), $endIndex - ($signIndex + strlen('&sign=')));
+ } else {
+ $result = Arr::wrapQuery($body, true);
+ $result = Collection::wrap($result);
+ $signature = $result->get('sign');
+ $result->forget('sign');
+ $result->forget('signType');
+ $data = $result->sortKeys()->toString();
+ }
+
+ return [
+ 'sign' => $signature,
+ 'data' => $data,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/AddRadarPlugin.php
new file mode 100644
index 0000000..4b5b569
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/AddRadarPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setRadar(new Request(
+ get_radar_method($payload) ?? 'POST',
+ get_unipay_url($config, $payload),
+ $this->getHeaders(),
+ get_unipay_body($payload),
+ ));
+
+ Logger::info('[Unipay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(): array
+ {
+ return [
+ 'User-Agent' => 'yansongda/pay-v3',
+ 'Content-Type' => 'application/x-www-form-urlencoded;charset=utf-8',
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/AddPayloadSignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/AddPayloadSignaturePlugin.php
new file mode 100644
index 0000000..62a9585
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/AddPayloadSignaturePlugin.php
@@ -0,0 +1,63 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ if (empty($payload) || $payload->isEmpty()) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 银联支付必要参数缺失。可能插件用错顺序,应该先使用 `业务插件`');
+ }
+
+ $rocket->mergePayload([
+ 'signature' => $this->getSignature($config['certs']['pkey'] ?? '', filter_params($rocket->getPayload(), fn ($k, $v) => 'signature' != $k)),
+ ]);
+
+ Logger::info('[Unipay][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getSignature(string $pkey, Collection $payload): string
+ {
+ if (empty($pkey)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 银联支付配置文件中未找到 `certs.pkey` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`');
+ }
+
+ $content = $payload->sortKeys()->toString();
+
+ openssl_sign(hash('sha256', $content), $sign, $pkey, 'sha256');
+
+ return base64_encode($sign);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/CallbackPlugin.php
new file mode 100644
index 0000000..2fc127b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/CallbackPlugin.php
@@ -0,0 +1,49 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPayload($params);
+
+ $collection = filter_params($params)->except('signature')->sortKeys();
+
+ verify_unipay_sign($config, $collection->toString(), $params['signature'] ?? '', $params['signPubKeyCert'] ?? null);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($rocket->getPayload());
+
+ Logger::info('[Unipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/H5/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/H5/PayPlugin.php
new file mode 100644
index 0000000..991670b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/H5/PayPlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/frontTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000201',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'currencyCode' => '156',
+ 'merId' => $config['mch_id'] ?? '',
+ 'channelType' => $payload?->get('channelType') ?? '07',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '01',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '01',
+ 'frontUrl' => $payload?->get('frontUrl') ?? $config['return_url'] ?? '',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][H5][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/CancelPlugin.php
new file mode 100644
index 0000000..6c82829
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/CancelPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '31',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPlugin.php
new file mode 100644
index 0000000..7d361bf
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '01',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '06',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][PosPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPreAuthPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPreAuthPlugin.php
new file mode 100644
index 0000000..b3f3f0f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/PosPreAuthPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000201',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '02',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '04',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][PosPreAuthPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/QueryPlugin.php
new file mode 100644
index 0000000..2594d39
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/QueryPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ '_sandbox_url' => 'https://101.231.204.80:5000/gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '00',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/RefundPlugin.php
new file mode 100644
index 0000000..ff445db
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/RefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '04',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][QrCode][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanFeePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanFeePlugin.php
new file mode 100644
index 0000000..a7de9cc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanFeePlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000601',
+ 'encoding' => 'utf-8',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '07',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '13',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '08',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][ScanFeePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPlugin.php
new file mode 100644
index 0000000..5a0e241
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '01',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '07',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][ScanPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreAuthPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreAuthPlugin.php
new file mode 100644
index 0000000..ee49436
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreAuthPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '02',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '05',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][ScanPreAuthPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreOrderPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreOrderPlugin.php
new file mode 100644
index 0000000..0c44b8b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/QrCode/ScanPreOrderPlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/order.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '08',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '01',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '01',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ 'frontUrl' => $payload?->get('frontUrl') ?? $config['return_url'] ?? null,
+ ]);
+
+ Logger::info('[Unipay][Pay][QrCode][ScanPreOrderPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/CancelPlugin.php
new file mode 100644
index 0000000..4379948
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/CancelPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'channelType' => $payload?->get('channelType') ?? '07',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '31',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][Web][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/PayPlugin.php
new file mode 100644
index 0000000..df20095
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/PayPlugin.php
@@ -0,0 +1,58 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->setDirection(ResponseDirection::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/frontTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000201',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'currencyCode' => '156',
+ 'channelType' => $payload?->get('channelType') ?? '07',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '01',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '01',
+ 'frontUrl' => $payload?->get('frontUrl') ?? $config['return_url'] ?? '',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][Web][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/QueryPlugin.php
new file mode 100644
index 0000000..e57e372
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/QueryPlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/queryTrans.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '00',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][Web][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/RefundPlugin.php
new file mode 100644
index 0000000..ab7b0b5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/Pay/Web/RefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(QueryPacker::class)
+ ->mergePayload([
+ '_url' => 'gateway/api/backTransReq.do',
+ 'encoding' => 'utf-8',
+ 'signature' => '',
+ 'bizType' => $payload?->get('bizType') ?? '000000',
+ 'accessType' => $payload?->get('accessType') ?? '0',
+ 'merId' => $config['mch_id'] ?? '',
+ 'channelType' => $payload?->get('channelType') ?? '07',
+ 'signMethod' => '01',
+ 'txnType' => $payload?->get('txnType') ?? '04',
+ 'txnSubType' => $payload?->get('txnSubType') ?? '00',
+ 'backUrl' => $payload?->get('backUrl') ?? $config['notify_url'] ?? '',
+ 'version' => '5.1.0',
+ ]);
+
+ Logger::info('[Unipay][Pay][Web][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/ResponseHtmlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/ResponseHtmlPlugin.php
new file mode 100644
index 0000000..339deb0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/ResponseHtmlPlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ $radar = $rocket->getRadar();
+ $payload = $rocket->getPayload();
+
+ $response = $this->buildHtml($radar->getUri()->__toString(), filter_params($payload));
+
+ $rocket->setDestination($response);
+
+ Logger::info('[Unipay][ResponseHtmlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function buildHtml(string $endpoint, Collection $payload): Response
+ {
+ $sHtml = "";
+ $sHtml .= "";
+
+ return new Response(200, [], $sHtml);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/StartPlugin.php
new file mode 100644
index 0000000..ed3252c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/StartPlugin.php
@@ -0,0 +1,91 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $tenant = get_tenant($params);
+
+ $rocket->mergePayload(array_merge($params, [
+ '_unpack_raw' => true,
+ 'certId' => $this->getCertId($tenant, $config),
+ ]));
+
+ Logger::info('[Unipay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws ServiceNotFoundException
+ */
+ public function getCertId(string $tenant, array $config): string
+ {
+ if (!empty($config['certs']['cert_id'])) {
+ return $config['certs']['cert_id'];
+ }
+
+ $certs = $this->getCerts($config);
+ $ssl = openssl_x509_parse($certs['cert'] ?? '');
+
+ if (false === $ssl) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 解析银联 `mch_cert_path` 失败,请检查参数是否正确');
+ }
+
+ $certs['cert_id'] = $ssl['serialNumber'] ?? '';
+
+ Pay::get(ConfigInterface::class)->set('unipay.'.$tenant.'.certs', $certs);
+
+ return $certs['cert_id'];
+ }
+
+ /**
+ * @return array ['cert' => 公钥, 'pkey' => 私钥, 'extracerts' => array]
+ *
+ * @throws InvalidConfigException
+ */
+ protected function getCerts(array $config): array
+ {
+ $path = $config['mch_cert_path'] ?? null;
+ $password = $config['mch_cert_password'] ?? null;
+
+ if (is_null($path) || is_null($password)) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 缺少银联配置 -- [mch_cert_path] or [mch_cert_password]');
+ }
+
+ if (false === openssl_pkcs12_read(file_get_contents($path), $certs, $password)) {
+ throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 读取银联 `mch_cert_path` 失败,请确认参数是否正确');
+ }
+
+ return $certs;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/VerifySignaturePlugin.php
new file mode 100644
index 0000000..aca6db4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Open/VerifySignaturePlugin.php
@@ -0,0 +1,60 @@
+ $rocket]);
+
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ $destination = $rocket->getDestination();
+
+ if (!$destination instanceof Collection) {
+ return $rocket;
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ verify_unipay_sign(
+ $config,
+ $destination->except('signature')->sortKeys()->toString(),
+ $destination->get('signature', ''),
+ $destination->get('signPubKeyCert')
+ );
+
+ Logger::info('[Unipay][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/AddPayloadSignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/AddPayloadSignaturePlugin.php
new file mode 100644
index 0000000..7c205e7
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/AddPayloadSignaturePlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ if (empty($payload) || $payload->isEmpty()) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 银联支付必要参数缺失。可能插件用错顺序,应该先使用 `业务插件`');
+ }
+
+ $rocket->mergePayload(['sign' => get_unipay_sign_qra($config, filter_params($payload)->all())]);
+
+ Logger::info('[Unipay][Qra][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/CallbackPlugin.php
new file mode 100644
index 0000000..3cc00ff
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/CallbackPlugin.php
@@ -0,0 +1,49 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $destination = filter_params($params);
+
+ if (isset($params['status']) && 0 == $params['status']) {
+ verify_unipay_sign_qra($config, $destination->all());
+ }
+
+ $rocket->setPayload($params)
+ ->setDirection(NoHttpRequestDirection::class)
+ ->setDestination($destination);
+
+ Logger::info('[Unipay][Qra][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/CancelPlugin.php
new file mode 100644
index 0000000..ecfe1b9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/CancelPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.micropay.reverse',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/PayPlugin.php
new file mode 100644
index 0000000..5ae7815
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/PayPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.micropay',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryOpenIdPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryOpenIdPlugin.php
new file mode 100644
index 0000000..d6dffd2
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryOpenIdPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.tools.authcodetoopenid',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][QueryOpenIdPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryPlugin.php
new file mode 100644
index 0000000..c8aebab
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.query',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryRefundPlugin.php
new file mode 100644
index 0000000..7d9280a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/QueryRefundPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.refundquery',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/RefundPlugin.php
new file mode 100644
index 0000000..58096b9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Pos/RefundPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.refund',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Pos][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/ClosePlugin.php
new file mode 100644
index 0000000..c6e486f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/ClosePlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.close',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Scan][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/PayPlugin.php
new file mode 100644
index 0000000..ea253de
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/PayPlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.native',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'notify_url' => $payload?->get('notify_url') ?? $config['notify_url'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Scan][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryPlugin.php
new file mode 100644
index 0000000..bff614d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.query',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryRefundPlugin.php
new file mode 100644
index 0000000..9458bbe
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/QueryRefundPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.refundquery',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Scan][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/RefundPlugin.php
new file mode 100644
index 0000000..d0c57b8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/Scan/RefundPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('unipay', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'https://qra.95516.com/pay/gateway',
+ 'service' => 'unified.trade.refund',
+ 'charset' => 'UTF-8',
+ 'sign_type' => 'MD5',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ ]);
+
+ Logger::info('[Unipay][Qra][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/StartPlugin.php
new file mode 100644
index 0000000..bc0339a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/StartPlugin.php
@@ -0,0 +1,26 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+
+ $rocket->mergePayload($params);
+
+ Logger::info('[Unipay][Qra][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/VerifySignaturePlugin.php
new file mode 100644
index 0000000..5f86622
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Unipay/Qra/VerifySignaturePlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $config = get_provider_config('unipay', $rocket->getParams());
+
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ verify_unipay_sign_qra($config, $rocket->getDestination()?->all() ?? []);
+
+ Logger::info('[Unipay][Qra][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/AddRadarPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/AddRadarPlugin.php
new file mode 100644
index 0000000..9b085ef
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/AddRadarPlugin.php
@@ -0,0 +1,76 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+
+ $rocket->setRadar(new Request(
+ get_wechat_method($payload),
+ get_wechat_url($config, $payload),
+ $this->getHeaders($payload),
+ get_wechat_body($payload),
+ ));
+
+ Logger::info('[Wechat][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function getHeaders(?Collection $payload): array
+ {
+ $headers = [
+ 'Accept' => 'application/json, text/plain, application/x-gzip',
+ 'User-Agent' => 'yansongda/pay-v3',
+ 'Content-Type' => 'application/json; charset=utf-8',
+ ];
+
+ // 当 body 里有加密内容时,需要传递此参数用于微信区分
+ if (!empty($serialNo = $payload?->get('_serial_no'))) {
+ $headers['Wechatpay-Serial'] = $serialNo;
+ }
+
+ if (!empty($authorization = $payload?->get('_authorization'))) {
+ $headers['Authorization'] = $authorization;
+ }
+
+ if (!empty($contentType = $payload?->get('_content_type'))) {
+ $headers['Content-Type'] = $contentType;
+ }
+
+ if (!empty($accept = $payload?->get('_accept'))) {
+ $headers['Accept'] = $accept;
+ }
+
+ return $headers;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/ResponsePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/ResponsePlugin.php
new file mode 100644
index 0000000..f284dc1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/ResponsePlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $this->validateResponse($rocket);
+
+ Logger::info('[Wechat][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidResponseException
+ */
+ protected function validateResponse(Rocket $rocket): void
+ {
+ $response = $rocket->getDestinationOrigin();
+
+ if ($response instanceof ResponseInterface
+ && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300)) {
+ throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '微信返回状态码异常,请检查参数是否错误', $rocket->getDestination());
+ }
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/StartPlugin.php
new file mode 100644
index 0000000..101deee
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/StartPlugin.php
@@ -0,0 +1,26 @@
+ $rocket]);
+
+ $rocket->mergePayload($rocket->getParams());
+
+ Logger::info('[Wechat][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/AddPayloadSignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/AddPayloadSignaturePlugin.php
new file mode 100644
index 0000000..9bc0d31
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/AddPayloadSignaturePlugin.php
@@ -0,0 +1,40 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ $rocket->mergePayload([
+ 'sign' => get_wechat_sign_v2($config, filter_params($rocket->getPayload())->all()),
+ ]);
+
+ Logger::info('[Wechat][V2][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ApplyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ApplyPlugin.php
new file mode 100644
index 0000000..36bc989
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ApplyPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'pay/pappayapply',
+ '_content_type' => 'application/xml',
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ 'sign_type' => 'MD5',
+ 'notify_url' => $payload?->get('notify_url') ?? $config['notify_url'] ?? '',
+ ]);
+
+ Logger::info('[Wechat][V2][Papay][Direct][ApplyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ContractOrderPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ContractOrderPlugin.php
new file mode 100644
index 0000000..fb7c1d5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/ContractOrderPlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'pay/contractorder',
+ '_content_type' => 'application/xml',
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'contract_appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'contract_mchid' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ 'sign_type' => 'MD5',
+ 'notify_url' => $payload?->get('notify_url') ?? $config['notify_url'] ?? '',
+ 'contract_notify_url' => $payload?->get('contract_notify_url') ?? $config['notify_url'] ?? '',
+ ]);
+
+ Logger::info('[Wechat][V2][Papay][Direct][ContractOrderPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/MiniOnlyContractPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/MiniOnlyContractPlugin.php
new file mode 100644
index 0000000..7f30d7a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Papay/Direct/MiniOnlyContractPlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDirection(NoHttpRequestDirection::class)
+ ->mergePayload([
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'notify_url' => $payload?->get('notify_url') ?? $config['notify_url'] ?? '',
+ 'timestamp' => time(),
+ ]);
+
+ Logger::info('[Wechat][V2][Papay][Direct][OnlyContractPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ /** @var Rocket $rocket */
+ $rocket = $next($rocket);
+
+ $rocket->setDestination($rocket->getPayload());
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/App/InvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/App/InvokePlugin.php
new file mode 100644
index 0000000..b30e2cf
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/App/InvokePlugin.php
@@ -0,0 +1,97 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id') ?? null;
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][V2][Pay][App][InvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][V2][Pay][App][InvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appid' => $this->getAppId($payload, $config),
+ 'partnerid' => $this->getPartnerId($payload, $config),
+ 'prepayid' => $prepayId,
+ 'package' => 'Sign=WXPay',
+ 'noncestr' => Str::random(32),
+ 'timestamp' => time().'',
+ ]);
+
+ $invokeConfig->set('sign', get_wechat_sign_v2($config, $invokeConfig->all()));
+
+ return $invokeConfig;
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['app_id'] ?? '';
+ }
+
+ protected function getPartnerId(?Collection $payload, array $config): string
+ {
+ return $payload?->get('_invoke_partnerid') ?? $config['mch_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Mini/InvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Mini/InvokePlugin.php
new file mode 100644
index 0000000..992fc72
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Mini/InvokePlugin.php
@@ -0,0 +1,91 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id') ?? null;
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][V2][Pay][Mini][InvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][V2][Pay][Mini][InvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appId' => $this->getAppId($payload, $config),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'MD5',
+ ]);
+
+ $invokeConfig->set('paySign', get_wechat_sign_v2($config, $invokeConfig->all()));
+
+ return $invokeConfig;
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_mini_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['mini_app_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/CancelPlugin.php
new file mode 100644
index 0000000..831cf4b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/CancelPlugin.php
@@ -0,0 +1,51 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'secapi/pay/reverse',
+ '_content_type' => 'application/xml',
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ 'sign_type' => 'MD5',
+ ]);
+
+ Logger::info('[Wechat][V2][Pay][Pos][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/PayPlugin.php
new file mode 100644
index 0000000..e3961f0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/PayPlugin.php
@@ -0,0 +1,51 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'pay/micropay',
+ '_content_type' => 'application/xml',
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ 'sign_type' => 'MD5',
+ ]);
+
+ Logger::info('[Wechat][V2][Pay][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/QueryPlugin.php
new file mode 100644
index 0000000..0262578
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Pos/QueryPlugin.php
@@ -0,0 +1,51 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload([
+ '_url' => 'pay/orderquery',
+ '_content_type' => 'application/xml',
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'nonce_str' => Str::random(32),
+ 'sign_type' => 'MD5',
+ ]);
+
+ Logger::info('[Wechat][V2][Pay][Pos][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Redpack/SendPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Redpack/SendPlugin.php
new file mode 100644
index 0000000..453c6c2
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/Pay/Redpack/SendPlugin.php
@@ -0,0 +1,81 @@
+ $rocket]);
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config, $params);
+ }
+
+ $rocket->setPacker(XmlPacker::class)
+ ->mergePayload(array_merge(
+ [
+ '_url' => 'mmpaymkttransfers/sendredpack',
+ '_content_type' => 'application/xml',
+ 'nonce_str' => Str::random(32),
+ '_http' => [
+ 'ssl_key' => $config['mch_secret_cert'],
+ 'cert' => $config['mch_public_cert_path'],
+ ],
+ ],
+ $data ?? $this->normal($config, $params)
+ ));
+
+ Logger::info('[Wechat][V2][Pay][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config, array $params): array
+ {
+ return [
+ 'wxappid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config, array $params): array
+ {
+ $wechatTypeKey = get_wechat_type_key($params);
+
+ return [
+ 'wxappid' => $config[$wechatTypeKey] ?? '',
+ 'mch_id' => $config['mch_id'] ?? '',
+ 'sub_mch_id' => $payload->get('sub_mch_id', $config['sub_mch_id'] ?? ''),
+ 'msgappid' => $config['sub_'.$wechatTypeKey],
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/VerifySignaturePlugin.php
new file mode 100644
index 0000000..9c7031e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V2/VerifySignaturePlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (!should_do_http_request($rocket->getDirection())) {
+ return $rocket;
+ }
+
+ verify_wechat_sign_v2($config, $rocket->getDestination()?->all() ?? []);
+
+ Logger::info('[Wechat][V2][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/AddPayloadSignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/AddPayloadSignaturePlugin.php
new file mode 100644
index 0000000..06d365b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/AddPayloadSignaturePlugin.php
@@ -0,0 +1,99 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+
+ $timestamp = time();
+ $random = Str::random(32);
+ $signContent = $this->getSignatureContent($config, $payload, $timestamp, $random);
+ $signature = $this->getSignature($config, $timestamp, $random, $signContent);
+
+ $rocket->mergePayload(['_authorization' => $signature]);
+
+ Logger::info('[Wechat][V3][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getSignatureContent(array $config, ?Collection $payload, int $timestamp, string $random): string
+ {
+ $url = get_wechat_url($config, $payload);
+ $urlPath = parse_url($url, PHP_URL_PATH);
+ $urlQuery = parse_url($url, PHP_URL_QUERY);
+
+ return get_wechat_method($payload)."\n"
+ .$urlPath.(empty($urlQuery) ? '' : '?'.$urlQuery)."\n"
+ .$timestamp."\n"
+ .$random."\n"
+ .get_wechat_body($payload)."\n";
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSignature(array $config, int $timestamp, string $random, string $contents): string
+ {
+ $mchPublicCertPath = $config['mch_public_cert_path'] ?? null;
+
+ if (empty($mchPublicCertPath)) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_public_cert_path]');
+ }
+
+ $ssl = openssl_x509_parse(get_public_cert($mchPublicCertPath));
+
+ if (empty($ssl['serialNumberHex'])) {
+ throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 解析微信配置 [mch_public_cert_path] 出错');
+ }
+
+ $auth = sprintf(
+ 'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
+ $config['mch_id'] ?? '',
+ $random,
+ $timestamp,
+ $ssl['serialNumberHex'],
+ get_wechat_sign($config, $contents),
+ );
+
+ return 'WECHATPAY2-SHA256-RSA2048 '.$auth;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/CallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/CallbackPlugin.php
new file mode 100644
index 0000000..107924c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/CallbackPlugin.php
@@ -0,0 +1,76 @@
+ $rocket]);
+
+ $this->init($rocket);
+
+ $params = $rocket->getParams();
+
+ /* @phpstan-ignore-next-line */
+ verify_wechat_sign($rocket->getDestinationOrigin(), $params);
+
+ $body = json_decode((string) $rocket->getDestination()->getBody(), true);
+
+ $rocket->setDirection(NoHttpRequestDirection::class)->setPayload(new Collection($body));
+
+ $body['resource'] = decrypt_wechat_resource($body['resource'] ?? [], get_provider_config('wechat', $params));
+
+ $rocket->setDestination(new Collection($body));
+
+ Logger::info('[Wechat][V3][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function init(Rocket $rocket): void
+ {
+ $request = $rocket->getParams()['_request'] ?? null;
+ $params = $rocket->getParams()['_params'] ?? [];
+
+ if (!$request instanceof ServerRequestInterface) {
+ throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID, '参数异常: 微信回调参数不正确');
+ }
+
+ $rocket->setDestination(clone $request)
+ ->setDestinationOrigin($request)
+ ->setParams($params);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/CompletePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/CompletePlugin.php
new file mode 100644
index 0000000..9e6aeb6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/CompletePlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $complaintId = $payload?->get('complaint_id') ?? null;
+
+ if (empty($complaintId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 反馈处理完成,参数缺少 `complaint_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/complete',
+ '_service_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/complete',
+ 'complainted_mchid' => $payload->get('complainted_mchid') ?? $config['mch_id'],
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][CompletePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/DeleteCallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/DeleteCallbackPlugin.php
new file mode 100644
index 0000000..4a29260
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/DeleteCallbackPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->setPayload([
+ '_method' => 'DELETE',
+ '_url' => 'v3/merchant-service/complaint-notifications',
+ '_service_url' => 'v3/merchant-service/complaint-notifications',
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][DeleteCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryCallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryCallbackPlugin.php
new file mode 100644
index 0000000..aa636ce
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryCallbackPlugin.php
@@ -0,0 +1,32 @@
+ $rocket]);
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/merchant-service/complaint-notifications',
+ '_service_url' => 'v3/merchant-service/complaint-notifications',
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][QueryCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryDetailPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryDetailPlugin.php
new file mode 100644
index 0000000..5764716
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryDetailPlugin.php
@@ -0,0 +1,72 @@
+ $rocket]);
+
+ $complaintId = $rocket->getPayload()?->get('complaint_id') ?? null;
+
+ if (empty($complaintId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询投诉单详情,参数缺少 `complaint_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/merchant-service/complaints-v2/'.$complaintId,
+ '_service_url' => 'v3/merchant-service/complaints-v2/'.$complaintId,
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][QueryDetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ /** @var Rocket $rocket */
+ $rocket = $next($rocket);
+
+ Logger::debug('[Wechat][Extend][Complaints][QueryDetailPlugin] 插件开始后置装载', ['rocket' => $rocket]);
+
+ $destination = $rocket->getDestination();
+
+ if ($destination instanceof Collection && !empty($payerPhone = $destination->get('payer_phone'))) {
+ $decryptPayerPhone = decrypt_wechat_contents($payerPhone, get_provider_config('wechat', $rocket->getParams()));
+
+ if (empty($decryptPayerPhone)) {
+ throw new InvalidConfigException(Exception::DECRYPT_WECHAT_ENCRYPTED_CONTENTS_INVALID, '参数异常: 查询投诉单详情,参数 `payer_phone` 解密失败');
+ }
+
+ $destination->set('payer_phone', $decryptPayerPhone);
+ }
+
+ Logger::debug('[Wechat][Extend][Complaints][QueryDetailPlugin] 插件后置装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryImagePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryImagePlugin.php
new file mode 100644
index 0000000..ab22edb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryImagePlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $mediaId = $rocket->getPayload()?->get('media_id') ?? null;
+
+ if (empty($mediaId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 图片请求接口,参数缺少 `media_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/merchant-service/images/'.$mediaId,
+ '_service_url' => 'v3/merchant-service/images/'.$mediaId,
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][QueryImagePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryNegotiationPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryNegotiationPlugin.php
new file mode 100644
index 0000000..43a771e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryNegotiationPlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $complaintId = $payload?->get('complaint_id') ?? null;
+
+ if (empty($complaintId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询投诉单协商历史,参数缺少 `complaint_id`');
+ }
+
+ $query = $this->normal($payload);
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/negotiation-historys'.$query,
+ '_service_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/negotiation-historys'.$query,
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][QueryNegotiationPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(Collection $payload): string
+ {
+ $query = filter_params($payload)->except('complaint_id')->query();
+
+ return empty($query) ? '' : '?'.$query;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryPlugin.php
new file mode 100644
index 0000000..190eb3d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/QueryPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询投诉单列表,缺少必要参数');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/merchant-service/complaints-v2?'.$query,
+ '_service_url' => 'v3/merchant-service/complaints-v2?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/ResponsePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/ResponsePlugin.php
new file mode 100644
index 0000000..6ed6050
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/ResponsePlugin.php
@@ -0,0 +1,52 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $complaintId = $payload?->get('complaint_id') ?? null;
+
+ if (empty($complaintId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 回复用户,参数缺少 `complaint_id`');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/response',
+ '_service_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/response',
+ 'complainted_mchid' => $payload->get('complainted_mchid', $config['mch_id']),
+ ])->exceptPayload('complaint_id');
+
+ Logger::info('[Wechat][Extend][Complaints][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/SetCallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/SetCallbackPlugin.php
new file mode 100644
index 0000000..cc366fa
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/SetCallbackPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/merchant-service/complaint-notifications',
+ '_service_url' => 'v3/merchant-service/complaint-notifications',
+ 'url' => $payload?->get('url', '') ?? '',
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][SetCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateCallbackPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateCallbackPlugin.php
new file mode 100644
index 0000000..06a41e5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateCallbackPlugin.php
@@ -0,0 +1,35 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ $rocket->setPayload([
+ '_method' => 'PUT',
+ '_url' => 'v3/merchant-service/complaint-notifications',
+ '_service_url' => 'v3/merchant-service/complaint-notifications',
+ 'url' => $payload?->get('url', '') ?? '',
+ ]);
+
+ Logger::info('[Wechat][Extend][Complaints][UpdateCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateRefundPlugin.php
new file mode 100644
index 0000000..6dcf317
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/Complaints/UpdateRefundPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $complaintId = $rocket->getPayload()?->get('complaint_id') ?? null;
+
+ if (empty($complaintId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 更新退款审批结果,参数缺少 `complaint_id`');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/update-refund-progress',
+ '_service_url' => 'v3/merchant-service/complaints-v2/'.$complaintId.'/update-refund-progress',
+ ])->exceptPayload('complaint_id');
+
+ Logger::info('[Wechat][Extend][Complaints][UpdateRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/AddReceiverPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/AddReceiverPlugin.php
new file mode 100644
index 0000000..f3877c5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/AddReceiverPlugin.php
@@ -0,0 +1,134 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少添加分账接收方参数');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/profitsharing/receivers/add',
+ '_service_url' => 'v3/profitsharing/receivers/add',
+ ],
+ $data ?? $this->normal($payload, $params, $config),
+ ));
+
+ Logger::info('[Wechat][Extend][ProfitSharing][AddReceiverPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function normal(Collection $payload, array $params, array $config): array
+ {
+ $data = [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ ];
+
+ if (!$payload->has('name')) {
+ return $data;
+ }
+
+ return array_merge($data, $this->encryptSensitiveData($params, $config, $payload));
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $wechatTypeKey = get_wechat_type_key($params);
+
+ $data = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'appid' => $config[$wechatTypeKey] ?? '',
+ ];
+
+ if ('PERSONAL_SUB_OPENID' === $payload->get('type')) {
+ $data['sub_appid'] = $config['sub_'.$wechatTypeKey] ?? '';
+ }
+
+ if (!$payload->has('name')) {
+ return $data;
+ }
+
+ return array_merge($data, $this->encryptSensitiveData($params, $config, $payload));
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function encryptSensitiveData(array $params, array $config, Collection $payload): array
+ {
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ $data['name'] = encrypt_wechat_contents($payload->get('name'), $publicKey);
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/CreatePlugin.php
new file mode 100644
index 0000000..1b3e346
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/CreatePlugin.php
@@ -0,0 +1,136 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少请求分账参数');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/profitsharing/orders',
+ '_service_url' => 'v3/profitsharing/orders',
+ ],
+ $data ?? $this->normal($payload, $params, $config),
+ ));
+
+ Logger::info('[Wechat][Extend][ProfitSharing][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function normal(Collection $payload, array $params, array $config): array
+ {
+ $data = [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ ];
+
+ if (!$payload->has('receivers.0.name')) {
+ return $data;
+ }
+
+ return array_merge($data, $this->encryptSensitiveData($params, $config, $payload));
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $wechatTypeKey = get_wechat_type_key($params);
+
+ $data = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'appid' => $config[$wechatTypeKey] ?? '',
+ ];
+
+ if ('PERSONAL_SUB_OPENID' === $payload->get('receivers.0.type')) {
+ $data['sub_appid'] = $config['sub_'.$wechatTypeKey] ?? '';
+ }
+
+ if (!$payload->has('receivers.0.name')) {
+ return $data;
+ }
+
+ return array_merge($data, $this->encryptSensitiveData($params, $config, $payload));
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function encryptSensitiveData(array $params, array $config, Collection $payload): array
+ {
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ foreach ($payload->get('receivers', []) as $key => $list) {
+ $data['receivers'][$key]['name'] = encrypt_wechat_contents($list['name'], $publicKey);
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DeleteReceiverPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DeleteReceiverPlugin.php
new file mode 100644
index 0000000..07dac6e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DeleteReceiverPlugin.php
@@ -0,0 +1,84 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少分账参数');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/profitsharing/receivers/delete',
+ '_service_url' => 'v3/profitsharing/receivers/delete',
+ ],
+ $data ?? $this->normal($params, $config),
+ ));
+
+ Logger::info('[Wechat][Extend][ProfitSharing][DeleteReceiverPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $params, array $config): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $wechatTypeKEY = get_wechat_type_key($params);
+
+ $data = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'appid' => $config[$wechatTypeKEY] ?? '',
+ ];
+
+ if ('PERSONAL_SUB_OPENID' === $payload->get('type')) {
+ $data['sub_appid'] = $config['sub_'.$wechatTypeKEY] ?? '';
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DownloadBillPlugin.php
new file mode 100644
index 0000000..c98b46d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载电子回单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Extend][ProfitSharing][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/GetBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/GetBillPlugin.php
new file mode 100644
index 0000000..09bac2d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/GetBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 分账 申请账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/profitsharing/bills?'.$query,
+ '_service_url' => 'v3/profitsharing/bills?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Extend][ProfitSharing][GetBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryAmountsPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryAmountsPlugin.php
new file mode 100644
index 0000000..ff604f9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryAmountsPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $transactionId = $rocket->getPayload()?->get('transaction_id');
+
+ if (is_null($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询剩余待分金额,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/profitsharing/transactions/'.$transactionId.'/amounts',
+ '_service_url' => 'v3/profitsharing/transactions/'.$transactionId.'/amounts',
+ ]);
+
+ Logger::info('[Wechat][Extend][ProfitSharing][QueryAmountsPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryMerchantConfigsPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryMerchantConfigsPlugin.php
new file mode 100644
index 0000000..3620fa5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryMerchantConfigsPlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $subMchId = $payload?->get('sub_mch_id') ?? $config['sub_mch_id'] ?? 'null';
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 查询最大分账比例,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/profitsharing/merchant-configs/'.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][Extend][ProfitSharing][QueryMerchantConfigsPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryPlugin.php
new file mode 100644
index 0000000..02ba6e0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outOrderNo = $payload?->get('out_order_no') ?? null;
+ $transactionId = $payload?->get('transaction_id') ?? null;
+ $subMchId = $payload?->get('sub_mchid') ?? $config['sub_mch_id'] ?? 'null';
+
+ if (empty($outOrderNo) || empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询分账结果, 缺少必要参数 `out_order_no`, `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/profitsharing/orders/'.$outOrderNo.'?transaction_id='.$transactionId,
+ '_service_url' => 'v3/profitsharing/orders/'.$outOrderNo.'?sub_mchid='.$subMchId.'&transaction_id='.$transactionId,
+ ]);
+
+ Logger::info('[Wechat][Extend][ProfitSharing][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryReturnPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryReturnPlugin.php
new file mode 100644
index 0000000..5509b2d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/QueryReturnPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outOrderNo = $payload?->get('out_order_no') ?? null;
+ $outReturnNo = $payload?->get('out_return_no') ?? null;
+ $subMchId = $payload?->get('sub_mchid') ?? $config['sub_mch_id'] ?? 'null';
+
+ if (empty($outOrderNo) || empty($outReturnNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询分账结果, 缺少必要参数 `out_order_no`, `out_return_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/profitsharing/return-orders/'.$outReturnNo.'?out_order_no='.$outOrderNo,
+ '_service_url' => 'v3/profitsharing/return-orders/'.$outReturnNo.'?sub_mchid='.$subMchId.'&out_order_no='.$outOrderNo,
+ ]);
+
+ Logger::info('[Wechat][Extend][ProfitSharing][QueryReturnPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/ReturnPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/ReturnPlugin.php
new file mode 100644
index 0000000..dd8c616
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/ReturnPlugin.php
@@ -0,0 +1,75 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少分账退回参数');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/profitsharing/return-orders',
+ '_service_url' => 'v3/profitsharing/return-orders',
+ ],
+ $data ?? $this->normal($payload, $config),
+ ));
+
+ Logger::info('[Wechat][Extend][ProfitSharing][ReturnPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(Collection $payload, array $config): array
+ {
+ return [
+ 'return_mchid' => $payload->get('return_mchid', $config['mch_id'] ?? ''),
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'return_mchid' => $payload->get('return_mchid', $config['mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/UnfreezePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/UnfreezePlugin.php
new file mode 100644
index 0000000..3c85140
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Extend/ProfitSharing/UnfreezePlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少分账解冻剩余资金参数');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/profitsharing/orders/unfreeze',
+ '_service_url' => 'v3/profitsharing/orders/unfreeze',
+ ],
+ $data ?? $this->normal(),
+ ));
+
+ Logger::info('[Wechat][Extend][ProfitSharing][UnfreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/QueryPlugin.php
new file mode 100644
index 0000000..d64a302
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/QueryPlugin.php
@@ -0,0 +1,44 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $mchId = $rocket->getPayload()?->get('mchid') ?? $config['mch_id'] ?? 'null';
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/callbacks?mchid='.$mchId,
+ '_service_url' => 'v3/marketing/favor/callbacks?mchid='.$mchId,
+ ]);
+
+ Logger::info('[Wechat][Marketing][Coupon][Callback][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/SetPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/SetPlugin.php
new file mode 100644
index 0000000..e066ecb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Callback/SetPlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/callbacks',
+ '_service_url' => 'v3/marketing/favor/callbacks',
+ 'mchid' => $payload?->get('mchid') ?? $config['mch_id'] ?? '',
+ 'notify_url' => $payload?->get('notify_url') ?? $config['notify_url'] ?? '',
+ ],
+ ));
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Callback][SetPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/DetailPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/DetailPlugin.php
new file mode 100644
index 0000000..84a2577
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/DetailPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $openId = $payload?->get('openid') ?? null;
+ $couponId = $payload?->get('coupon_id') ?? null;
+ $appId = $payload?->get('appid') ?? $config[get_wechat_type_key($params)] ?? 'null';
+
+ if (empty($openId) || empty($couponId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券详情,参数缺少 `openid` 或 `coupon_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/users/'.$openId.'/coupons/'.$couponId.'?appid='.$appId,
+ '_service_url' => 'v3/marketing/favor/users/'.$openId.'/coupons/'.$couponId.'?appid='.$appId,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Coupons][DetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/QueryUserPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/QueryUserPlugin.php
new file mode 100644
index 0000000..dc94d70
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/QueryUserPlugin.php
@@ -0,0 +1,66 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $openId = $payload?->get('openid') ?? null;
+
+ if (empty($openId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 根据商户号查用户的券,参数缺少 `openid`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/users/'.$openId.'/coupons?'.$this->normal($payload, $params, $config),
+ '_service_url' => 'v3/marketing/favor/users/'.$openId.'/coupons?'.$this->normal($payload, $params, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Coupons][QueryUserPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(Collection $payload, array $params, array $config): string
+ {
+ $appId = $payload->get('appid');
+
+ if (is_null($appId)) {
+ $payload->set('appid', $config[get_wechat_type_key($params)] ?? '');
+ }
+
+ return filter_params($payload)->except('openid')->query();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/SendPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/SendPlugin.php
new file mode 100644
index 0000000..178690e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Coupons/SendPlugin.php
@@ -0,0 +1,70 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $openId = $payload?->get('openid') ?? null;
+
+ if (empty($openId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 发放指定批次的代金券,参数缺少 `openid`');
+ }
+
+ $rocket->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/users/'.$openId.'/coupons',
+ '_service_url' => 'v3/marketing/favor/users/'.$openId.'/coupons',
+ ],
+ $this->normal($payload, $params, $config),
+ ));
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Coupons][SendPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(Collection $payload, array $params, array $config): array
+ {
+ if (empty($payload->get('appid'))) {
+ $payload->set('appid', $config[get_wechat_type_key($params)] ?? '');
+ }
+
+ if (empty($payload->get('stock_creator_mchid'))) {
+ $payload->set('stock_creator_mchid', $config['mch_id'] ?? '');
+ }
+
+ return $payload->except('openid')->all();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/CreatePlugin.php
new file mode 100644
index 0000000..3d34944
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/CreatePlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $belongMerchant = $rocket->getPayload()?->get('belong_merchant') ?? $config['mch_id'];
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/coupon-stocks',
+ '_service_url' => 'v3/marketing/favor/coupon-stocks',
+ 'belong_merchant' => $belongMerchant,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/PausePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/PausePlugin.php
new file mode 100644
index 0000000..780b1a1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/PausePlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+ $stockCreatorMchId = $payload?->get('stock_creator_mchid') ?? $config['mch_id'] ?? '';
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 暂停代金券批次,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/pause',
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/pause',
+ 'stock_creator_mchid' => $stockCreatorMchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][PausePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryDetailPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryDetailPlugin.php
new file mode 100644
index 0000000..4b311d4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryDetailPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+ $mchId = $payload?->get('stock_creator_mchid') ?? $config['mch_id'] ?? 'null';
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券批次详情,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'?stock_creator_mchid='.$mchId,
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'?stock_creator_mchid='.$mchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryDetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryItemsPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryItemsPlugin.php
new file mode 100644
index 0000000..ef68a98
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryItemsPlugin.php
@@ -0,0 +1,65 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券可用单品,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/items?'.$this->normal($payload, $config),
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/items?'.$this->normal($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryItemsPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ public function normal(Collection $payload, array $config): string
+ {
+ $stockCreatorMchId = $payload->get('stock_creator_mchid');
+
+ if (is_null($stockCreatorMchId)) {
+ $payload->set('stock_creator_mchid', $config['mch_id'] ?? '');
+ }
+
+ return filter_params($payload)->except('stock_id')->query();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryMerchantsPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryMerchantsPlugin.php
new file mode 100644
index 0000000..0777a46
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryMerchantsPlugin.php
@@ -0,0 +1,65 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券可用商户,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/merchants?'.$this->normal($payload, $config),
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/merchants?'.$this->normal($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryMerchantsPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ public function normal(Collection $payload, array $config): string
+ {
+ $stockCreatorMchId = $payload->get('stock_creator_mchid');
+
+ if (is_null($stockCreatorMchId)) {
+ $payload->set('stock_creator_mchid', $config['mch_id'] ?? '');
+ }
+
+ return filter_params($payload)->except('stock_id')->query();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryPlugin.php
new file mode 100644
index 0000000..503c238
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryPlugin.php
@@ -0,0 +1,64 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少代金券相关参数');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks?'.$this->normal($payload, $config),
+ '_service_url' => 'v3/marketing/favor/stocks?'.$this->normal($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ public function normal(Collection $payload, array $config): string
+ {
+ $stockCreatorMchId = $payload->get('stock_creator_mchid');
+
+ if (is_null($stockCreatorMchId)) {
+ $payload->set('stock_creator_mchid', $config['mch_id'] ?? '');
+ }
+
+ return filter_params($payload)->query();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryRefundFlowPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryRefundFlowPlugin.php
new file mode 100644
index 0000000..243fa30
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryRefundFlowPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $stockId = $rocket->getPayload()?->get('stock_id') ?? null;
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载批次退款明细,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/refund-flow',
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/refund-flow',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryRefundFlowPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryUseFlowPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryUseFlowPlugin.php
new file mode 100644
index 0000000..0de6bc4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/QueryUseFlowPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $stockId = $rocket->getPayload()?->get('stock_id') ?? null;
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载批次核销明细,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/use-flow',
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/use-flow',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][QueryUseFlowPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/RestartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/RestartPlugin.php
new file mode 100644
index 0000000..c1393dc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/RestartPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+ $stockCreatorMchId = $payload?->get('stock_creator_mchid') ?? $config['mch_id'] ?? '';
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 激活代金券,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/restart',
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/restart',
+ 'stock_creator_mchid' => $stockCreatorMchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][RestartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/StartPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/StartPlugin.php
new file mode 100644
index 0000000..8fae020
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Coupon/Stock/StartPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $stockId = $payload?->get('stock_id') ?? null;
+
+ if (empty($stockId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 激活代金券,参数缺少 `stock_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/marketing/favor/stocks/'.$stockId.'/start',
+ '_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/start',
+ 'stock_creator_mchid' => $payload->get('stock_creator_mchid') ?? $config['mch_id'] ?? '',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Coupon][Stock][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryDayEndPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryDayEndPlugin.php
new file mode 100644
index 0000000..c1a5c82
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryDayEndPlugin.php
@@ -0,0 +1,67 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $accountType = $payload?->get('account_type') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 查询电商平台账户日终余额,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (empty($accountType)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询电商平台账户日终余额,参数缺少 `account_type`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/merchant/fund/dayendbalance/'.$accountType.$this->service($payload),
+ ]);
+
+ Logger::info('[Wechat][Marketing][ECommerceBalance][QueryDayEndPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function service(Collection $payload): string
+ {
+ $query = filter_params($payload)->except('account_type');
+
+ if ($query->isEmpty()) {
+ return '';
+ }
+
+ return '?'.$query->query();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryPlugin.php
new file mode 100644
index 0000000..942064a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceBalance/QueryPlugin.php
@@ -0,0 +1,53 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $accountType = $rocket->getPayload()?->get('account_type') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 查询电商平台账户实时余额,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (empty($accountType)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询电商平台账户实时余额,参数缺少 `account_type`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/merchant/fund/balance/'.$accountType,
+ ]);
+
+ Logger::info('[Wechat][Marketing][ECommerceBalance][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ApplyPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ApplyPlugin.php
new file mode 100644
index 0000000..c7aa7d3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ApplyPlugin.php
@@ -0,0 +1,60 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $subMchId = $payload?->get('sub_mchid') ?? $config['sub_mch_id'] ?? '';
+ $spAppId = $payload?->get('sp_appid') ?? $config[get_wechat_type_key($params)] ?? '';
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 平台收付通(退款)-申请退款,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 平台收付通(退款)-申请退款,缺少必要参数');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_service_url' => 'v3/ecommerce/refunds/apply',
+ 'sub_mchid' => $subMchId,
+ 'sp_appid' => $spAppId,
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][ECommerceRefund][ApplyPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryByWxPlugin.php
new file mode 100644
index 0000000..6caf710
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryByWxPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $refundId = $payload?->get('refund_id') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 平台收付通(退款)-查询单笔退款(按微信支付退款单号),只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (is_null($refundId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 平台收付通(退款)-查询单笔退款(按微信支付退款单号),缺少必要参数 `refund_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/ecommerce/refunds/id/'.$refundId.'?sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+
+ Logger::info('[Wechat][Marketing][ECommerceRefund][QueryBatchByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryPlugin.php
new file mode 100644
index 0000000..00f910f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 平台收付通(退款)-查询单笔退款(按商户退款单号),只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (is_null($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 平台收付通(退款)-查询单笔退款(按商户退款单号),缺少必要参数 `out_refund_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/ecommerce/refunds/out-refund-no/'.$outRefundNo.'?sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][ECommerceRefund][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryReturnAdvancePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryReturnAdvancePlugin.php
new file mode 100644
index 0000000..f4a22a3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/QueryReturnAdvancePlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $refundId = $payload?->get('refund_id') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 平台收付通(退款)-查询垫付回补结果,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (is_null($refundId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 平台收付通(退款)-查询垫付回补结果,缺少必要参数 `refund_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_service_url' => 'v3/ecommerce/refunds/'.$refundId.'/return-advance?sub_mchid='.$payload->get('sub_mchid', $config['sub_mch_id']),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][ECommerceRefund][QueryReturnAdvancePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ReturnAdvancePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ReturnAdvancePlugin.php
new file mode 100644
index 0000000..9a256f5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/ECommerceRefund/ReturnAdvancePlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $refundId = $payload?->get('refund_id') ?? null;
+
+ if (Pay::MODE_NORMAL === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE, '参数异常: 平台收付通(退款)-垫付退款回补,只支持服务商模式,当前配置为普通商户模式');
+ }
+
+ if (is_null($refundId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 平台收付通(退款)-垫付退款回补,缺少必要参数 `refund_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_service_url' => 'v3/ecommerce/refunds/'.$refundId.'/return-advance',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][ECommerceRefund][ReturnAdvancePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/CreatePlugin.php
new file mode 100644
index 0000000..c197545
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/CreatePlugin.php
@@ -0,0 +1,80 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+
+ $rocket->mergePayload(array_merge([
+ '_method' => 'POST',
+ '_url' => 'v3/new-tax-control-fapiao/fapiao-applications',
+ ], $this->encryptSensitiveData($payload, $params, $config)));
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][Blockchain][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ * @throws DecryptException
+ */
+ protected function encryptSensitiveData(?Collection $payload, array $params, array $config): array
+ {
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ $phone = $payload?->get('buyer_information.phone') ?? null;
+ $email = $payload?->get('buyer_information.email') ?? null;
+
+ if (!is_null($phone)) {
+ $data['buyer_information']['phone'] = encrypt_wechat_contents($phone, $publicKey);
+ }
+
+ if (!is_null($email)) {
+ $data['buyer_information']['email'] = encrypt_wechat_contents($email, $publicKey);
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/DownloadPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/DownloadPlugin.php
new file mode 100644
index 0000000..485029e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/DownloadPlugin.php
@@ -0,0 +1,46 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $downloadUrl = $payload?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载发票文件,缺少 `download_url` 参数');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl.'&'.filter_params($payload)->except('download_url')->query(),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][Blockchain][DownloadPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetBaseInformationPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetBaseInformationPlugin.php
new file mode 100644
index 0000000..a4df4f8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetBaseInformationPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/merchant/base-information',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][Blockchain][GetBaseInformationPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetDownloadInfoPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetDownloadInfoPlugin.php
new file mode 100644
index 0000000..27d5d8f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetDownloadInfoPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $applyId = $payload?->get('fapiao_apply_id') ?? null;
+ $fapiaoId = $payload?->get('fapiao_id') ?? null;
+
+ if (empty($applyId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询电子发票,参数缺少 `fapiao_apply_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/fapiao-applications/'.$applyId.'/fapiao-files'.(empty($fapiaoId) ? '' : '?fapiao_id='.$fapiaoId),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][GetDownloadInfoPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetTaxCodePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetTaxCodePlugin.php
new file mode 100644
index 0000000..c40a3a1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/GetTaxCodePlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $offset = $payload?->get('offset') ?? null;
+ $limit = $payload?->get('limit') ?? null;
+
+ if (empty($offset) || empty($limit)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 获取商户可开具的商品和服务税收分类编码对照表,缺少 `offset` 或 `limit` 参数');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/merchant/tax-codes?offset='.$offset.'&limit='.$limit,
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][Blockchain][GetTaxCodePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/ReversePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/ReversePlugin.php
new file mode 100644
index 0000000..170ca48
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/Blockchain/ReversePlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $applyId = $payload?->get('fapiao_apply_id') ?? null;
+
+ if (empty($applyId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 冲红电子发票,参数缺少 `fapiao_apply_id`');
+ }
+
+ $rocket->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/new-tax-control-fapiao/fapiao-applications/'.$applyId.'/reverse',
+ ],
+ filter_params($payload)->except('fapiao_apply_id')->all(),
+ ));
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][Blockchain][ReversePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/CreateCardTemplatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/CreateCardTemplatePlugin.php
new file mode 100644
index 0000000..d7436f1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/CreateCardTemplatePlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/new-tax-control-fapiao/card-template',
+ 'card_appid' => $payload?->get('card_appid') ?? $config['mp_app_id'],
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][CreateCardTemplatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/GetTitleUrlPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/GetTitleUrlPlugin.php
new file mode 100644
index 0000000..ed6ccbf
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/GetTitleUrlPlugin.php
@@ -0,0 +1,64 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+
+ if (empty($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 获取抬头填写链接,缺少必要参数');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/user-title/title-url?'.$this->getQuery($payload, $params)->query(),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][GetTitleUrlPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws ServiceNotFoundException
+ */
+ protected function getQuery(Collection $payload, array $params): Collection
+ {
+ $config = get_provider_config('wechat', $params);
+
+ return filter_params($payload)->merge([
+ 'appid' => $payload->get('appid', $config[get_wechat_type_key($params)] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryConfigPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryConfigPlugin.php
new file mode 100644
index 0000000..7ac20b2
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryConfigPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/merchant/development-config',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][QueryConfigPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryPlugin.php
new file mode 100644
index 0000000..c0863f0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $applyId = $payload?->get('fapiao_apply_id') ?? null;
+ $fapiaoId = $payload?->get('fapiao_id') ?? null;
+
+ if (empty($applyId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询电子发票,参数缺少 `fapiao_apply_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/fapiao-applications/'.$applyId.(empty($fapiaoId) ? '' : '?fapiao_id='.$fapiaoId),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryUserTitlePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryUserTitlePlugin.php
new file mode 100644
index 0000000..750cce3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/QueryUserTitlePlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (empty($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 获取用户填写的抬头,缺少必要参数');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/new-tax-control-fapiao/user-title?'.filter_params($payload)->query(),
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][QueryUserTitlePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/UpdateConfigPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/UpdateConfigPlugin.php
new file mode 100644
index 0000000..72a2d4c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Fapiao/UpdateConfigPlugin.php
@@ -0,0 +1,30 @@
+ $rocket]);
+
+ $rocket->mergePayload([
+ '_method' => 'PATCH',
+ '_url' => 'v3/new-tax-control-fapiao/merchant/development-config',
+ ]);
+
+ Logger::info('[Wechat][V3][Marketing][Fapiao][UpdateConfigPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CancelPlugin.php
new file mode 100644
index 0000000..91cd699
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CancelPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outBillNo = $payload?->get('out_bill_no') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 撤销转账,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($outBillNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 撤销转账,参数缺少 `out_bill_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'POST',
+ '_url' => 'v3/fund-app/mch-transfer/transfer-bills/out-bill-no/'.$outBillNo.'/cancel',
+ ]);
+
+ Logger::info('[Wechat][Marketing][MchTransfer][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CreatePlugin.php
new file mode 100644
index 0000000..fd2de0c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/CreatePlugin.php
@@ -0,0 +1,102 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 发起商家转账,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($payload) || $payload->isEmpty()) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 发起商家转账参数,参数缺失');
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/fund-app/mch-transfer/transfer-bills',
+ 'appid' => $payload->get('appid', $config[get_wechat_type_key($params)] ?? ''),
+ ],
+ $this->normal($params, $payload)
+ ));
+
+ Logger::info('[Wechat][Marketing][MchTransfer][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ */
+ protected function normal(array $params, Collection $payload): array
+ {
+ if (!$payload->has('user_name')) {
+ return [];
+ }
+
+ return $this->encryptSensitiveData($params, $payload);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function encryptSensitiveData(array $params, Collection $payload): array
+ {
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ $data['user_name'] = encrypt_wechat_contents($payload->get('user_name'), $publicKey);
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeAndroidPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeAndroidPlugin.php
new file mode 100644
index 0000000..b8ac3ea
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeAndroidPlugin.php
@@ -0,0 +1,77 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $destination = $rocket->getDestination();
+ $packageInfo = $destination?->get('package_info');
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: Android调起用户确认收款,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($packageInfo)) {
+ Logger::error('[Wechat][V3][Marketing][MchTransfer][InvokeAndroidPlugin] Android调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('fail_reason') ?? 'Android调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $params, $config, $packageInfo));
+
+ Logger::info('[Wechat][V3][Marketing][MchTransfer][InvokeAndroidPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function getInvokeConfig(?Collection $payload, array $params, array $config, string $packageInfo): Config
+ {
+ return new Config([
+ 'businessType' => 'requestMerchantTransfer',
+ 'query' => http_build_query([
+ 'appId' => $payload?->get('_invoke_appId') ?? $config[get_wechat_type_key($params)] ?? '',
+ 'mchId' => $payload?->get('_invoke_mchId') ?? $config['mch_id'] ?? '',
+ 'package' => $packageInfo,
+ ]),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeIosPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeIosPlugin.php
new file mode 100644
index 0000000..f124cc9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeIosPlugin.php
@@ -0,0 +1,77 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $destination = $rocket->getDestination();
+ $packageInfo = $destination?->get('package_info');
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: iOS调起用户确认收款,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($packageInfo)) {
+ Logger::error('[Wechat][V3][Marketing][MchTransfer][InvokeIosPlugin] iOS调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('fail_reason') ?? 'iOS调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $params, $config, $packageInfo));
+
+ Logger::info('[Wechat][V3][Marketing][MchTransfer][InvokeIosPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function getInvokeConfig(?Collection $payload, array $params, array $config, string $packageInfo): Config
+ {
+ return new Config([
+ 'businessType' => 'requestMerchantTransfer',
+ 'query' => http_build_query([
+ 'appId' => $payload?->get('_invoke_appId') ?? $config[get_wechat_type_key($params)] ?? '',
+ 'mchId' => $payload?->get('_invoke_mchId') ?? $config['mch_id'] ?? '',
+ 'package' => $packageInfo,
+ ]),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeJsapiPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeJsapiPlugin.php
new file mode 100644
index 0000000..ac7c5ac
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/InvokeJsapiPlugin.php
@@ -0,0 +1,74 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $destination = $rocket->getDestination();
+ $packageInfo = $destination?->get('package_info');
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: JSAPI调起用户确认收款,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($packageInfo)) {
+ Logger::error('[Wechat][V3][Marketing][MchTransfer][InvokeJsapiPlugin] JSAPI调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('fail_reason') ?? 'JSAPI调起用户确认收款失败:响应缺少 `package_info` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $params, $config, $packageInfo));
+
+ Logger::info('[Wechat][V3][Marketing][MchTransfer][InvokeJsapiPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ protected function getInvokeConfig(?Collection $payload, array $params, array $config, string $packageInfo): Config
+ {
+ return new Config([
+ 'appId' => $payload?->get('_invoke_appId') ?? $config[get_wechat_type_key($params)] ?? '',
+ 'mchId' => $payload?->get('_invoke_mchId') ?? $config['mch_id'] ?? '',
+ 'package' => $packageInfo,
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryByWxPlugin.php
new file mode 100644
index 0000000..a112900
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryByWxPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $transferBillNo = $payload?->get('transfer_bill_no') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过微信单号查询转账单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($transferBillNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过微信单号查询转账单,参数缺少 `transfer_bill_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/'.$transferBillNo,
+ ]);
+
+ Logger::info('[Wechat][Marketing][MchTransfer][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryPlugin.php
new file mode 100644
index 0000000..f7b8020
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/MchTransfer/QueryPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outBillNo = $payload?->get('out_bill_no') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过商户单号查询转账单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($outBillNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过商户单号查询转账单,参数缺少 `out_bill_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/fund-app/mch-transfer/transfer-bills/out-bill-no/'.$outBillNo,
+ ]);
+
+ Logger::info('[Wechat][Marketing][MchTransfer][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryByWxPlugin.php
new file mode 100644
index 0000000..d0e9490
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryByWxPlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $batchId = $payload?->get('batch_id') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过微信批次单号查询批次单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($batchId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过微信批次单号查询批次单,参数缺少 `batch_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer/batches/batch-id/'.$batchId.'?'.filter_params($payload)->except('batch_id')->query(),
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Batch][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryPlugin.php
new file mode 100644
index 0000000..d787f3e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Batch/QueryPlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outBatchNo = $payload?->get('out_batch_no') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过商家批次单号查询批次单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($outBatchNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过商家批次单号查询批次单,参数缺少 `out_batch_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer/batches/out-batch-no/'.$outBatchNo.'?'.filter_params($payload)->except('out_batch_no')->query(),
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Batch][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/CreatePlugin.php
new file mode 100644
index 0000000..a617255
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/CreatePlugin.php
@@ -0,0 +1,109 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 发起商家转账,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($payload) || $payload->isEmpty()) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 发起商家转账参数,参数缺失');
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/transfer/batches',
+ 'appid' => $payload->get('appid', $config[get_wechat_type_key($params)] ?? ''),
+ ],
+ $this->normal($params, $payload)
+ ));
+
+ Logger::info('[Wechat][Marketing][Transfer][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ */
+ protected function normal(array $params, Collection $payload): array
+ {
+ if (!$payload->has('transfer_detail_list.0.user_name')) {
+ return [];
+ }
+
+ return $this->encryptSensitiveData($params, $payload);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function encryptSensitiveData(array $params, Collection $payload): array
+ {
+ $data['transfer_detail_list'] = $payload->get('transfer_detail_list', []);
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ foreach ($data['transfer_detail_list'] as $key => $list) {
+ if (!empty($list['user_name'])) {
+ $data['transfer_detail_list'][$key]['user_name'] = encrypt_wechat_contents($list['user_name'], $publicKey);
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryByWxPlugin.php
new file mode 100644
index 0000000..e2d7505
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryByWxPlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $batchId = $payload?->get('batch_id') ?? null;
+ $detailId = $payload?->get('detail_id') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过微信明细单号查询明细单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($batchId) || empty($detailId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过微信明细单号查询明细单,参数缺少 `batch_id` 或 `detail_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer/batches/batch-id/'.$batchId.'/details/detail-id/'.$detailId,
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Detail][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryPlugin.php
new file mode 100644
index 0000000..d401a68
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Detail/QueryPlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+ $payload = $rocket->getPayload();
+ $outBatchNo = $payload?->get('out_batch_no') ?? null;
+ $outDetailNo = $payload?->get('out_detail_no') ?? null;
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 通过商家明细单号查询明细单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($outBatchNo) || empty($outDetailNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 通过商家明细单号查询明细单,参数缺少 `out_batch_no` 或 `out_detail_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer/batches/out-batch-no/'.$outBatchNo.'/details/out-detail-no/'.$outDetailNo,
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Detail][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/DownloadReceiptPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/DownloadReceiptPlugin.php
new file mode 100644
index 0000000..049ea5f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/DownloadReceiptPlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 下载电子回单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载电子回单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][DownloadReceiptPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/CreatePlugin.php
new file mode 100644
index 0000000..9775669
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/CreatePlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 转账账单电子回单申请受理接口,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/transfer/bill-receipt',
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Receipt][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/QueryPlugin.php
new file mode 100644
index 0000000..c4032cc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/Receipt/QueryPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $outBatchNo = $rocket->getPayload()?->get('out_batch_no') ?? null;
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 查询转账账单电子回单接口,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (empty($outBatchNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询转账账单电子回单接口,参数缺少 `out_batch_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer/bill-receipt/'.$outBatchNo,
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][Receipt][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/CreatePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/CreatePlugin.php
new file mode 100644
index 0000000..28cd2a8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/CreatePlugin.php
@@ -0,0 +1,50 @@
+ $rocket]);
+
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 受理转账明细电子回单,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/transfer-detail/electronic-receipts',
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][ReceiptDetail][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/QueryPlugin.php
new file mode 100644
index 0000000..efe4670
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Marketing/Transfer/ReceiptDetail/QueryPlugin.php
@@ -0,0 +1,56 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $rocket->getParams());
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE, '参数异常: 查询转账明细电子回单受理结果API,只支持普通商户模式,当前配置为服务商模式');
+ }
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询转账明细电子回单受理结果API,参数为空');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/transfer-detail/electronic-receipts?'.filter_params($payload)->query(),
+ ]);
+
+ Logger::info('[Wechat][Marketing][Transfer][ReceiptDetail][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/ClosePlugin.php
new file mode 100644
index 0000000..e6209e8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/ClosePlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 关闭订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload(array_merge(
+ [
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_method' => 'POST',
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][App][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/DownloadBillPlugin.php
new file mode 100644
index 0000000..e9ed826
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetFundBillPlugin.php
new file mode 100644
index 0000000..be05cfd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetTradeBillPlugin.php
new file mode 100644
index 0000000..dfba012
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/InvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/InvokePlugin.php
new file mode 100644
index 0000000..1347d97
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/InvokePlugin.php
@@ -0,0 +1,111 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][V3][Pay][App][InvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][V3][Pay][App][InvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appid' => $this->getAppId($payload, $config),
+ 'partnerid' => $this->getPartnerId($payload, $config),
+ 'prepayid' => $prepayId,
+ 'package' => 'Sign=WXPay',
+ 'noncestr' => Str::random(32),
+ 'timestamp' => time().'',
+ ]);
+
+ $invokeConfig->set('sign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appid', '')."\n"
+ .$invokeConfig->get('timestamp', '')."\n"
+ .$invokeConfig->get('noncestr', '')."\n"
+ .$invokeConfig->get('prepayid', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['app_id'] ?? '';
+ }
+
+ protected function getPartnerId(?Collection $payload, array $config): string
+ {
+ return $payload?->get('_invoke_partnerid') ?? $config['mch_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/PayPlugin.php
new file mode 100644
index 0000000..2b8008d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/PayPlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: APP下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/app',
+ '_service_url' => 'v3/pay/partner/transactions/app',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][App][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'appid' => $config['app_id'] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_appid' => $config['app_id'] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryByWxPlugin.php
new file mode 100644
index 0000000..b0a7bd7
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryByWxPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $transactionId = $payload?->get('transaction_id') ?? null;
+
+ if (empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 通过微信订单号查询订单,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/id/'.$transactionId.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/id/'.$transactionId.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? 'null',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? 'null',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryPlugin.php
new file mode 100644
index 0000000..eb2ded4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 通过商户订单号查询订单,参数缺少 `out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryRefundPlugin.php
new file mode 100644
index 0000000..9c09f15
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][App][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/RefundPlugin.php
new file mode 100644
index 0000000..c66896e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/App/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][V3][Pay][App][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/DownloadPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/DownloadPlugin.php
new file mode 100644
index 0000000..3ec1650
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/DownloadPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Bill][DownloadPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetFundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetFundPlugin.php
new file mode 100644
index 0000000..2f51eb5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetFundPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Bill][GetFundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetTradePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetTradePlugin.php
new file mode 100644
index 0000000..d0f54cc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Bill/GetTradePlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: App 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Bill][GetTradePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppInvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppInvokePlugin.php
new file mode 100644
index 0000000..8a98c15
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppInvokePlugin.php
@@ -0,0 +1,111 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][Pay][Combine][AppInvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][Pay][Combine][AppInvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appid' => $this->getAppId($payload, $config),
+ 'partnerid' => $this->getPartnerId($payload, $config),
+ 'prepayid' => $prepayId,
+ 'package' => 'Sign=WXPay',
+ 'noncestr' => Str::random(32),
+ 'timestamp' => time().'',
+ ]);
+
+ $invokeConfig->set('sign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appid', '')."\n"
+ .$invokeConfig->get('timestamp', '')."\n"
+ .$invokeConfig->get('noncestr', '')."\n"
+ .$invokeConfig->get('prepayid', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['app_id'] ?? '';
+ }
+
+ protected function getPartnerId(?Collection $payload, array $config): string
+ {
+ return $payload?->get('_invoke_partnerid') ?? $config['mch_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppPayPlugin.php
new file mode 100644
index 0000000..5044c4c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/AppPayPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: APP合单 下单,参数为空');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/app',
+ '_service_url' => 'v3/combine-transactions/app',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ 'combine_appid' => $payload->get('combine_appid', $config['app_id'] ?? ''),
+ 'combine_mchid' => $payload->get('combine_mchid', $config['mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/ClosePlugin.php
new file mode 100644
index 0000000..8af5263
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/ClosePlugin.php
@@ -0,0 +1,57 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $combineOutTradeNo = $payload?->get('combine_out_trade_no') ?? null;
+
+ if (empty($combineOutTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单关单,参数缺少 `combine_out_trade_no`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/out-trade-no/'.$combineOutTradeNo.'/close',
+ '_service_url' => 'v3/combine-transactions/out-trade-no/'.$combineOutTradeNo.'/close',
+ 'combine_appid' => $payload->get('combine_appid', $config[get_wechat_type_key($params)] ?? ''),
+ ])
+ ->exceptPayload('combine_out_trade_no');
+
+ Logger::info('[Wechat][Pay][Combine][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/DownloadBillPlugin.php
new file mode 100644
index 0000000..91b47fd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetFundBillPlugin.php
new file mode 100644
index 0000000..4c0850f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetTradeBillPlugin.php
new file mode 100644
index 0000000..f93b06d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/H5PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/H5PayPlugin.php
new file mode 100644
index 0000000..76efcd5
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/H5PayPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5合单 下单,参数为空');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/h5',
+ '_service_url' => 'v3/combine-transactions/h5',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ 'combine_appid' => $payload->get('combine_appid', $config[get_wechat_type_key($params)] ?? ''),
+ 'combine_mchid' => $payload->get('combine_mchid', $config['mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][H5PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiInvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiInvokePlugin.php
new file mode 100644
index 0000000..2c469e1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiInvokePlugin.php
@@ -0,0 +1,106 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][Pay][Combine][JsapiInvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $params, $config, $prepayId));
+
+ Logger::info('[Wechat][Pay][Combine][JsapiInvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $params, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appId' => $this->getAppId($payload, $config, $params),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'RSA',
+ ]);
+
+ $invokeConfig->set('paySign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appId', '')."\n"
+ .$invokeConfig->get('timeStamp', '')."\n"
+ .$invokeConfig->get('nonceStr', '')."\n"
+ .$invokeConfig->get('package', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config, array $params): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_'.get_wechat_type_key($params)] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config[get_wechat_type_key($params)] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiPayPlugin.php
new file mode 100644
index 0000000..f4323bd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/JsapiPayPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi合单 下单,参数为空');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/jsapi',
+ '_service_url' => 'v3/combine-transactions/jsapi',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ 'combine_appid' => $payload->get('combine_appid', $config[get_wechat_type_key($params)] ?? ''),
+ 'combine_mchid' => $payload->get('combine_mchid', $config['mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][JsapiPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniInvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniInvokePlugin.php
new file mode 100644
index 0000000..7828158
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniInvokePlugin.php
@@ -0,0 +1,105 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][Pay][Combine][MiniInvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][Pay][Combine][MiniInvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appId' => $this->getAppId($payload, $config),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'RSA',
+ ]);
+
+ $invokeConfig->set('paySign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appId', '')."\n"
+ .$invokeConfig->get('timeStamp', '')."\n"
+ .$invokeConfig->get('nonceStr', '')."\n"
+ .$invokeConfig->get('package', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_mini_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['mini_app_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniPayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniPayPlugin.php
new file mode 100644
index 0000000..3ec706b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/MiniPayPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini合单 下单,参数为空');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/jsapi',
+ '_service_url' => 'v3/combine-transactions/jsapi',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ 'combine_appid' => $payload->get('combine_appid', $config['mini_app_id'] ?? ''),
+ 'combine_mchid' => $payload->get('combine_mchid', $config['mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][MiniPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/NativePayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/NativePayPlugin.php
new file mode 100644
index 0000000..c435b67
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/NativePayPlugin.php
@@ -0,0 +1,55 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native合单 下单,参数为空');
+ }
+
+ $rocket->mergePayload([
+ '_method' => 'POST',
+ '_url' => 'v3/combine-transactions/native',
+ '_service_url' => 'v3/combine-transactions/native',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ 'combine_appid' => $payload->get('combine_appid', $config[get_wechat_type_key($params)] ?? ''),
+ 'combine_mchid' => $payload->get('combine_mchid', $config['mch_id'] ?? ''),
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][NativePayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryPlugin.php
new file mode 100644
index 0000000..c1d5b46
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryPlugin.php
@@ -0,0 +1,43 @@
+ $rocket]);
+
+ $combineOutTradeNo = $rocket->getPayload()?->get('combine_out_trade_no') ?? null;
+
+ if (empty($combineOutTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单查询,参数缺少 `combine_out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/combine-transactions/out-trade-no/'.$combineOutTradeNo,
+ '_service_url' => 'v3/combine-transactions/out-trade-no/'.$combineOutTradeNo,
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryRefundPlugin.php
new file mode 100644
index 0000000..9f1c637
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][Pay][Combine][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/RefundPlugin.php
new file mode 100644
index 0000000..08efaa8
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Combine/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 合单 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][Pay][Combine][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/ClosePlugin.php
new file mode 100644
index 0000000..4b946bb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/ClosePlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 关闭订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/close',
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][H5][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/DownloadBillPlugin.php
new file mode 100644
index 0000000..326f4de
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetFundBillPlugin.php
new file mode 100644
index 0000000..b58789d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetTradeBillPlugin.php
new file mode 100644
index 0000000..6f420c6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/PayPlugin.php
new file mode 100644
index 0000000..6fde0cb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/PayPlugin.php
@@ -0,0 +1,81 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/h5',
+ '_service_url' => 'v3/pay/partner/transactions/h5',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ ],
+ $data ?? $this->normal($params, $config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][H5][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $params, array $config): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $configKey = get_wechat_type_key($params);
+
+ return [
+ 'sp_appid' => $config[$configKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryByWxPlugin.php
new file mode 100644
index 0000000..aa81152
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryByWxPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $transactionId = $payload?->get('transaction_id') ?? null;
+
+ if (empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 通过微信订单号查询订单,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/id/'.$transactionId.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/id/'.$transactionId.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? 'null',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? 'null',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryPlugin.php
new file mode 100644
index 0000000..9786a4b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 通过商户订单号查询订单,参数缺少 `out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryRefundPlugin.php
new file mode 100644
index 0000000..c282160
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][H5][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/RefundPlugin.php
new file mode 100644
index 0000000..9dbd42f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/H5/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: H5 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][V3][Pay][H5][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/ClosePlugin.php
new file mode 100644
index 0000000..4aad2b0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/ClosePlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 关闭订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/close',
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][Pay][Jsapi][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/DownloadBillPlugin.php
new file mode 100644
index 0000000..46328e6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetFundBillPlugin.php
new file mode 100644
index 0000000..2fa7b89
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetTradeBillPlugin.php
new file mode 100644
index 0000000..584807b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/InvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/InvokePlugin.php
new file mode 100644
index 0000000..9762c4f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/InvokePlugin.php
@@ -0,0 +1,106 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][V3][Pay][Jsapi][InvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $params, $config, $prepayId));
+
+ Logger::info('[Wechat][V3][Pay][Jsapi][InvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $params, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appId' => $this->getAppId($payload, $config, $params),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'RSA',
+ ]);
+
+ $invokeConfig->set('paySign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appId', '')."\n"
+ .$invokeConfig->get('timeStamp', '')."\n"
+ .$invokeConfig->get('nonceStr', '')."\n"
+ .$invokeConfig->get('package', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config, array $params): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_'.get_wechat_type_key($params)] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config[get_wechat_type_key($params)] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/PayPlugin.php
new file mode 100644
index 0000000..7642cf3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/PayPlugin.php
@@ -0,0 +1,87 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config, $params);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/jsapi',
+ '_service_url' => 'v3/pay/partner/transactions/jsapi',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ ],
+ $data ?? $this->normal($config, $params)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Jsapi][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config, array $params): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config, array $params): array
+ {
+ $wechatTypeKey = get_wechat_type_key($params);
+
+ $data = [
+ 'sp_appid' => $config[$wechatTypeKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+
+ if ($payload->has('payer.sub_openid')) {
+ $data['sub_appid'] = $config['sub_'.$wechatTypeKey] ?? '';
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryByWxPlugin.php
new file mode 100644
index 0000000..bbd08cd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryByWxPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $transactionId = $payload?->get('transaction_id') ?? null;
+
+ if (empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 通过微信订单号查询订单,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/id/'.$transactionId.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/id/'.$transactionId.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? 'null',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? 'null',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryPlugin.php
new file mode 100644
index 0000000..c52825a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 通过商户订单号查询订单,参数缺少 `out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryRefundPlugin.php
new file mode 100644
index 0000000..f050eb3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][Pay][Jsapi][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/RefundPlugin.php
new file mode 100644
index 0000000..b419cd6
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Jsapi/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Jsapi 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][Pay][Jsapi][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/ClosePlugin.php
new file mode 100644
index 0000000..cfaf85c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/ClosePlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 关闭订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/close',
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][Pay][Mini][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/DownloadBillPlugin.php
new file mode 100644
index 0000000..27f82e3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetFundBillPlugin.php
new file mode 100644
index 0000000..4c3f701
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetTradeBillPlugin.php
new file mode 100644
index 0000000..7cd9cbe
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/InvokePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/InvokePlugin.php
new file mode 100644
index 0000000..793de71
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/InvokePlugin.php
@@ -0,0 +1,105 @@
+ $rocket]);
+
+ $destination = $rocket->getDestination();
+ $prepayId = $destination?->get('prepay_id');
+
+ if (is_null($prepayId)) {
+ Logger::error('[Wechat][Pay][Mini][InvokePlugin] 预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+
+ throw new InvalidResponseException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, $destination?->get('message') ?? '预下单失败:响应缺少 `prepay_id` 参数,请自行检查参数是否符合微信要求', $destination?->all() ?? null);
+ }
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $rocket->setDestination($this->getInvokeConfig($payload, $config, $prepayId));
+
+ Logger::info('[Wechat][Pay][Mini][InvokePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ * @throws Throwable 生成随机串失败
+ */
+ protected function getInvokeConfig(?Collection $payload, array $config, string $prepayId): Config
+ {
+ $invokeConfig = new Config([
+ 'appId' => $this->getAppId($payload, $config),
+ 'timeStamp' => time().'',
+ 'nonceStr' => Str::random(32),
+ 'package' => 'prepay_id='.$prepayId,
+ 'signType' => 'RSA',
+ ]);
+
+ $invokeConfig->set('paySign', $this->getSign($invokeConfig, $config));
+
+ return $invokeConfig;
+ }
+
+ /**
+ * @throws InvalidConfigException
+ */
+ protected function getSign(Collection $invokeConfig, array $config): string
+ {
+ $contents = $invokeConfig->get('appId', '')."\n"
+ .$invokeConfig->get('timeStamp', '')."\n"
+ .$invokeConfig->get('nonceStr', '')."\n"
+ .$invokeConfig->get('package', '')."\n";
+
+ return get_wechat_sign($config, $contents);
+ }
+
+ protected function getAppId(?Collection $payload, array $config): string
+ {
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ return $payload?->get('_invoke_appid') ?? $config['sub_mini_app_id'] ?? '';
+ }
+
+ return $payload?->get('_invoke_appid') ?? $config['mini_app_id'] ?? '';
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/PayPlugin.php
new file mode 100644
index 0000000..5549c0d
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/PayPlugin.php
@@ -0,0 +1,84 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/jsapi',
+ '_service_url' => 'v3/pay/partner/transactions/jsapi',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][Pay][Mini][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'appid' => $config['mini_app_id'] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ $data = [
+ 'sp_appid' => $config['mini_app_id'] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+
+ if ($payload->has('payer.sub_openid')) {
+ $data['sub_appid'] = $config['sub_mini_app_id'] ?? '';
+ }
+
+ return $data;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryByWxPlugin.php
new file mode 100644
index 0000000..90d6da1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryByWxPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $transactionId = $payload?->get('transaction_id') ?? null;
+
+ if (empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 通过微信订单号查询订单,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/id/'.$transactionId.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/id/'.$transactionId.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][QueryByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? 'null',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? 'null',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryPlugin.php
new file mode 100644
index 0000000..22450b1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 通过商户订单号查询订单,参数缺少 `out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryRefundPlugin.php
new file mode 100644
index 0000000..9302ea0
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][Pay][Mini][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/RefundPlugin.php
new file mode 100644
index 0000000..306f978
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Mini/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Mini 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][Pay][Mini][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/ClosePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/ClosePlugin.php
new file mode 100644
index 0000000..62c65eb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/ClosePlugin.php
@@ -0,0 +1,78 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 关闭订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/close',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/close',
+ ],
+ $data ?? $this->normal($config)
+ ));
+
+ Logger::info('[Wechat][Pay][Native][ClosePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): array
+ {
+ return [
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/DownloadBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/DownloadBillPlugin.php
new file mode 100644
index 0000000..7c4e11f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/DownloadBillPlugin.php
@@ -0,0 +1,45 @@
+ $rocket]);
+
+ $downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
+
+ if (empty($downloadUrl)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 下载交易对账单,参数缺少 `download_url`');
+ }
+
+ $rocket->setDirection(OriginResponseDirection::class)
+ ->setPayload([
+ '_method' => 'GET',
+ '_url' => $downloadUrl,
+ '_service_url' => $downloadUrl,
+ ]);
+
+ Logger::info('[Wechat][Pay][Native][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetFundBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetFundBillPlugin.php
new file mode 100644
index 0000000..af88b6a
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetFundBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 申请资金账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/fundflowbill?'.$query,
+ '_service_url' => 'v3/bill/fundflowbill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Native][GetFundBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetTradeBillPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetTradeBillPlugin.php
new file mode 100644
index 0000000..88fa99b
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/GetTradeBillPlugin.php
@@ -0,0 +1,47 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 申请交易账单,参数为空');
+ }
+
+ $query = filter_params($payload)->query();
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/bill/tradebill?'.$query,
+ '_service_url' => 'v3/bill/tradebill?'.$query,
+ ]);
+
+ Logger::info('[Wechat][Pay][Native][GetTradeBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/PayPlugin.php
new file mode 100644
index 0000000..151b281
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/PayPlugin.php
@@ -0,0 +1,81 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 下单,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/native',
+ '_service_url' => 'v3/pay/partner/transactions/native',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? ''),
+ ],
+ $data ?? $this->normal($params, $config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Native][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $params, array $config): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $configKey = get_wechat_type_key($params);
+
+ return [
+ 'sp_appid' => $config[$configKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryByWxPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryByWxPlugin.php
new file mode 100644
index 0000000..e5b67ad
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryByWxPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $transactionId = $payload?->get('transaction_id') ?? null;
+
+ if (empty($transactionId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 通过微信订单号查询订单,参数缺少 `transaction_id`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/id/'.$transactionId.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/id/'.$transactionId.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][Pay][Native][QueryBatchByWxPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? 'null',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? 'null',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? 'null'),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryPlugin.php
new file mode 100644
index 0000000..50a14d7
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryPlugin.php
@@ -0,0 +1,68 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 通过商户订单号查询订单,参数缺少 `out_trade_no`');
+ }
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'?'.$this->normal($config),
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'?'.$this->service($payload, $config),
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Native][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $config): string
+ {
+ return http_build_query([
+ 'mchid' => $config['mch_id'] ?? '',
+ ]);
+ }
+
+ protected function service(Collection $payload, array $config): string
+ {
+ return http_build_query([
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryRefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryRefundPlugin.php
new file mode 100644
index 0000000..ff323c3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/QueryRefundPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][Pay][Native][QueryRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/RefundPlugin.php
new file mode 100644
index 0000000..4a22300
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Native/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: Native 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Native][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/CancelPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/CancelPlugin.php
new file mode 100644
index 0000000..2da3ac3
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/CancelPlugin.php
@@ -0,0 +1,83 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ $outTradeNo = $payload?->get('out_trade_no') ?? null;
+
+ if (empty($outTradeNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 付款码支付撤销订单,参数缺少 `out_trade_no`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->setPayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/reverse',
+ '_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/reverse',
+ ],
+ $data ?? $this->normal($params, $config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Pos][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $params, array $config): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $configKey = get_wechat_type_key($params);
+
+ return [
+ 'sp_appid' => $config[$configKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/PayPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/PayPlugin.php
new file mode 100644
index 0000000..c97f7cb
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Pos/PayPlugin.php
@@ -0,0 +1,81 @@
+ $rocket]);
+
+ $payload = $rocket->getPayload();
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 付款码支付,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($payload, $params, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/pay/transactions/codepay',
+ '_service_url' => 'v3/pay/partner/transactions/codepay',
+ ],
+ $data ?? $this->normal($params, $config)
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(array $params, array $config): array
+ {
+ return [
+ 'appid' => $config[get_wechat_type_key($params)] ?? '',
+ 'mchid' => $config['mch_id'] ?? '',
+ ];
+ }
+
+ protected function service(Collection $payload, array $params, array $config): array
+ {
+ $configKey = get_wechat_type_key($params);
+
+ return [
+ 'sp_appid' => $config[$configKey] ?? '',
+ 'sp_mchid' => $config['mch_id'] ?? '',
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/QueryPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/QueryPlugin.php
new file mode 100644
index 0000000..52a4122
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/QueryPlugin.php
@@ -0,0 +1,54 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+ $outRefundNo = $payload?->get('out_refund_no') ?? null;
+
+ if (empty($outRefundNo)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询退款订单,参数缺少 `out_refund_no`');
+ }
+
+ $subMchId = $payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/refund/domestic/refunds/'.$outRefundNo,
+ '_service_url' => 'v3/refund/domestic/refunds/'.$outRefundNo.'?sub_mchid='.$subMchId,
+ ]);
+
+ Logger::info('[Wechat][V3][Pay][Refund][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundAbnormalPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundAbnormalPlugin.php
new file mode 100644
index 0000000..9203689
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundAbnormalPlugin.php
@@ -0,0 +1,118 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $payload = $rocket->getPayload();
+ $config = get_provider_config('wechat', $params);
+ $refundId = $payload?->get('refund_id') ?? null;
+
+ if (empty($refundId)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 发起异常退款,参数缺少 `refund_id`');
+ }
+
+ if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
+ $data = $this->service($params, $config, $payload);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds/'.$refundId.'/apply-abnormal-refund',
+ '_service_url' => 'v3/refund/domestic/refunds/'.$refundId.'/apply-abnormal-refund',
+ ],
+ $data ?? $this->normal($params, $config, $payload)
+ ))->exceptPayload('refund_id');
+
+ Logger::info('[Wechat][V3][Pay][Refund][RefundAbnormalPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ */
+ protected function normal(array $params, array $config, Collection $payload): array
+ {
+ return $this->encryptSensitiveData($params, $config, $payload);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function service(array $params, array $config, Collection $payload): array
+ {
+ $data = [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+
+ return array_merge($data, $this->encryptSensitiveData($params, $config, $payload));
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws DecryptException
+ * @throws InvalidConfigException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ protected function encryptSensitiveData(array $params, array $config, Collection $payload): array
+ {
+ if ($payload->has('bank_account') && $payload->has('real_name')) {
+ $data['_serial_no'] = get_wechat_serial_no($params);
+
+ $config = get_provider_config('wechat', $params);
+ $publicKey = get_wechat_public_key($config, $data['_serial_no']);
+
+ $data['real_name'] = encrypt_wechat_contents($payload->get('real_name'), $publicKey);
+ $data['bank_account'] = encrypt_wechat_contents($payload->get('bank_account'), $publicKey);
+ }
+
+ return $data ?? [];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundPlugin.php
new file mode 100644
index 0000000..51bf5b4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/Pay/Refund/RefundPlugin.php
@@ -0,0 +1,73 @@
+ $rocket]);
+
+ $params = $rocket->getParams();
+ $config = get_provider_config('wechat', $params);
+ $payload = $rocket->getPayload();
+
+ if (is_null($payload)) {
+ throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 退款申请,参数为空');
+ }
+
+ if (Pay::MODE_SERVICE === $config['mode']) {
+ $data = $this->service($payload, $config);
+ }
+
+ $rocket->mergePayload(array_merge(
+ [
+ '_method' => 'POST',
+ '_url' => 'v3/refund/domestic/refunds',
+ '_service_url' => 'v3/refund/domestic/refunds',
+ 'notify_url' => $payload->get('notify_url', $config['notify_url'] ?? null),
+ ],
+ $data ?? $this->normal()
+ ));
+
+ Logger::info('[Wechat][V3][Pay][Refund][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+
+ protected function normal(): array
+ {
+ return [];
+ }
+
+ protected function service(Collection $payload, array $config): array
+ {
+ return [
+ 'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/VerifySignaturePlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/VerifySignaturePlugin.php
new file mode 100644
index 0000000..9f651bc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/VerifySignaturePlugin.php
@@ -0,0 +1,48 @@
+ $rocket]);
+
+ if (!should_do_http_request($rocket->getDirection()) || is_null($rocket->getDestinationOrigin())) {
+ return $rocket;
+ }
+
+ verify_wechat_sign($rocket->getDestinationOrigin(), $rocket->getParams());
+
+ Logger::info('[Wechat][V3][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $rocket;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/WechatPublicCertsPlugin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/WechatPublicCertsPlugin.php
new file mode 100644
index 0000000..7d9af61
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Plugin/Wechat/V3/WechatPublicCertsPlugin.php
@@ -0,0 +1,27 @@
+ $rocket]);
+
+ $rocket->setPayload([
+ '_method' => 'GET',
+ '_url' => 'v3/certificates',
+ ]);
+
+ Logger::info('[Wechat][V3][WechatPublicCertsPlugin] 插件装载完毕', ['rocket' => $rocket]);
+
+ return $next($rocket);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Alipay.php b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Alipay.php
new file mode 100644
index 0000000..c93004e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Alipay.php
@@ -0,0 +1,175 @@
+ 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ Pay::MODE_SANDBOX => 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8',
+ Pay::MODE_SERVICE => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
+ ];
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params): null|Collection|MessageInterface|Rocket
+ {
+ $plugin = '\Yansongda\Pay\Shortcut\Alipay\\'.Str::studly($shortcut).'Shortcut';
+
+ return Artful::shortcut($plugin, ...$params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ return Artful::artful($plugins, $params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function query(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function cancel(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('cancel', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function close(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('close', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('alipay', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new CallbackReceived('alipay', $request->all(), $params, null));
+
+ return $this->pay([CallbackPlugin::class], $request->merge($params)->all());
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function appCallback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection
+ {
+ $request = $this->getCallbackParams($contents);
+
+ return $this->pay([AppCallbackPlugin::class], $request->merge($params)->all());
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(200, [], 'success');
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [StartPlugin::class],
+ $plugins,
+ [FormatPayloadBizContentPlugin::class, AddPayloadSignaturePlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class],
+ );
+ }
+
+ protected function getCallbackParams(null|array|ServerRequestInterface $contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return Collection::wrap('GET' === $contents->getMethod() ? $contents->getQueryParams()
+ : $contents->getParsedBody());
+ }
+
+ $request = ServerRequest::fromGlobals();
+
+ return Collection::wrap(
+ array_merge($request->getQueryParams(), $request->getParsedBody())
+ );
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Douyin.php b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Douyin.php
new file mode 100644
index 0000000..dfe77c1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Douyin.php
@@ -0,0 +1,154 @@
+ 'https://developer.toutiao.com/',
+ Pay::MODE_SANDBOX => 'https://open-sandbox.douyin.com/',
+ Pay::MODE_SERVICE => 'https://developer.toutiao.com/',
+ ];
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params): null|Collection|MessageInterface|Rocket
+ {
+ $plugin = '\Yansongda\Pay\Shortcut\Douyin\\'.Str::studly($shortcut).'Shortcut';
+
+ return Artful::shortcut($plugin, ...$params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ return Artful::artful($plugins, $params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function query(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('douyin', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function cancel(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, '参数异常: 抖音不支持 cancel API');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function close(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, '参数异常: 抖音不支持 close API');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('douyin', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new CallbackReceived('douyin', $request->all(), $params, null));
+
+ return $this->pay([CallbackPlugin::class], $request->merge($params)->all());
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(
+ 200,
+ ['Content-Type' => 'application/json'],
+ json_encode(['err_no' => 0, 'err_tips' => 'success']),
+ );
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [StartPlugin::class],
+ $plugins,
+ [AddPayloadSignaturePlugin::class, AddPayloadBodyPlugin::class, AddRadarPlugin::class, ResponsePlugin::class, ParserPlugin::class],
+ );
+ }
+
+ protected function getCallbackParams(null|array|ServerRequestInterface $contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if (!$contents instanceof ServerRequestInterface) {
+ $contents = ServerRequest::fromGlobals();
+ }
+
+ $body = Collection::wrap($contents->getParsedBody());
+
+ if ($body->isNotEmpty()) {
+ return $body;
+ }
+
+ return Collection::wrapJson((string) $contents->getBody());
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Jsb.php b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Jsb.php
new file mode 100644
index 0000000..1190846
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Jsb.php
@@ -0,0 +1,155 @@
+ 'https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm',
+ Pay::MODE_SANDBOX => 'https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm',
+ ];
+
+ /**
+ * @param mixed $name
+ * @param mixed $params
+ *
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call($name, $params): null|Collection|MessageInterface|Rocket
+ {
+ $plugin = '\Yansongda\Pay\Shortcut\Jsb\\'.Str::studly($name).'Shortcut';
+
+ return Artful::shortcut($plugin, ...$params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ return Artful::artful($plugins, $params);
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [StartPlugin::class],
+ $plugins,
+ [AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class],
+ );
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function cancel(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Jsb does not support cancel api');
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function close(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Jsb does not support close api');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('jsb', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new CallbackReceived('jsb', $request->all(), $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ ['request' => $request, 'params' => $params]
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(
+ 200,
+ ['Content-Type' => 'text/html'],
+ 'success',
+ );
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function query(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('jsb', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ protected function getCallbackParams($contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return Collection::wrap($contents->getParsedBody());
+ }
+
+ $request = ServerRequest::fromGlobals();
+
+ return Collection::wrap($request->getParsedBody());
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Unipay.php b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Unipay.php
new file mode 100644
index 0000000..08d7cce
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Unipay.php
@@ -0,0 +1,156 @@
+ 'https://gateway.95516.com/',
+ Pay::MODE_SANDBOX => 'https://gateway.test.95516.com/',
+ Pay::MODE_SERVICE => 'https://gateway.95516.com/',
+ ];
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params): null|Collection|MessageInterface|Rocket
+ {
+ $plugin = '\Yansongda\Pay\Shortcut\Unipay\\'.Str::studly($shortcut).'Shortcut';
+
+ return Artful::shortcut($plugin, ...$params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ return Artful::artful($plugins, $params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function query(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function cancel(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('cancel', [$order]);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function close(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, '参数异常: 银联不支持 close API');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('unipay', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new CallbackReceived('unipay', $request->all(), $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ $request->merge($params)->all()
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(200, [], 'success');
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [StartPlugin::class],
+ $plugins,
+ [AddPayloadSignaturePlugin::class, AddPayloadBodyPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ParserPlugin::class],
+ );
+ }
+
+ protected function getCallbackParams(null|array|ServerRequestInterface $contents = null): Collection
+ {
+ if (is_array($contents)) {
+ return Collection::wrap($contents);
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return Collection::wrap($contents->getParsedBody());
+ }
+
+ $request = ServerRequest::fromGlobals();
+
+ return Collection::wrap($request->getParsedBody());
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Wechat.php b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Wechat.php
new file mode 100644
index 0000000..fb88094
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Provider/Wechat.php
@@ -0,0 +1,171 @@
+ 'https://api.mch.weixin.qq.com/',
+ Pay::MODE_SANDBOX => 'https://api.mch.weixin.qq.com/sandboxnew/',
+ Pay::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
+ ];
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function __call(string $shortcut, array $params): null|Collection|MessageInterface|Rocket
+ {
+ $plugin = '\Yansongda\Pay\Shortcut\Wechat\\'.Str::studly($shortcut).'Shortcut';
+
+ return Artful::shortcut($plugin, ...$params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket
+ {
+ return Artful::artful($plugins, $params);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function query(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('wechat', __METHOD__, $order, null));
+
+ return $this->__call('query', [$order]);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ public function cancel(array $order): Collection|Rocket
+ {
+ throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, '参数异常: 微信不支持 cancel API');
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function close(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('wechat', __METHOD__, $order, null));
+
+ $this->__call('close', [$order]);
+
+ return new Collection();
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ * @throws ServiceNotFoundException
+ */
+ public function refund(array $order): Collection|Rocket
+ {
+ Event::dispatch(new MethodCalled('wechat', __METHOD__, $order, null));
+
+ return $this->__call('refund', [$order]);
+ }
+
+ /**
+ * @throws ContainerException
+ * @throws InvalidParamsException
+ */
+ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket
+ {
+ $request = $this->getCallbackParams($contents);
+
+ Event::dispatch(new CallbackReceived('wechat', clone $request, $params, null));
+
+ return $this->pay(
+ [CallbackPlugin::class],
+ ['_request' => $request, '_params' => $params]
+ );
+ }
+
+ public function success(): ResponseInterface
+ {
+ return new Response(
+ 200,
+ ['Content-Type' => 'application/json'],
+ json_encode(['code' => 'SUCCESS', 'message' => '成功']),
+ );
+ }
+
+ public function mergeCommonPlugins(array $plugins): array
+ {
+ return array_merge(
+ [StartPlugin::class],
+ $plugins,
+ [AddPayloadBodyPlugin::class, AddPayloadSignaturePlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class],
+ );
+ }
+
+ protected function getCallbackParams(null|array|ServerRequestInterface $contents = null): ServerRequestInterface
+ {
+ if (is_array($contents) && isset($contents['body'], $contents['headers'])) {
+ return new ServerRequest('POST', 'http://localhost', $contents['headers'], $contents['body']);
+ }
+
+ if (is_array($contents)) {
+ return new ServerRequest('POST', 'http://localhost', [], json_encode($contents));
+ }
+
+ if ($contents instanceof ServerRequestInterface) {
+ return $contents;
+ }
+
+ return ServerRequest::fromGlobals();
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Service/AlipayServiceProvider.php b/server/addons/epay/library/v3new/Yansongda/Pay/Service/AlipayServiceProvider.php
new file mode 100644
index 0000000..b74a0bd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Service/AlipayServiceProvider.php
@@ -0,0 +1,24 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->posPlugins();
+ }
+
+ protected function agreementPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AgreementCancelPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function authorizationPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AuthorizationCancelPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniCancelPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function posPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosCancelPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function scanPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanCancelPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/CloseShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/CloseShortcut.php
new file mode 100644
index 0000000..016d849
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/CloseShortcut.php
@@ -0,0 +1,159 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function agreementPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AgreementClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function authorizationPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AuthorizationClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function posPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function scanPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function h5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5ClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebClosePlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/H5Shortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/H5Shortcut.php
new file mode 100644
index 0000000..ebc99f4
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/H5Shortcut.php
@@ -0,0 +1,30 @@
+refundPlugins();
+ }
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function agreementPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AgreementQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function authorizationPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AuthorizationQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function facePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ FaceQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function posPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function scanPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function h5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5QueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function transferPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ TransferQueryPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundPlugins(): array
+ {
+ return $this->refundWebPlugins();
+ }
+
+ protected function refundAppPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundAuthorizationPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AuthorizationQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundMiniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundPosPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundScanPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundH5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5QueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundWebPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebQueryRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/RefundShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/RefundShortcut.php
new file mode 100644
index 0000000..bf30461
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/RefundShortcut.php
@@ -0,0 +1,174 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function agreementPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AgreementRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function authorizationPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AuthorizationRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function posPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function scanPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function h5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5RefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function transferPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ FundTransferRefundPlugin::class,
+ FormatPayloadBizContentPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/ScanShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/ScanShortcut.php
new file mode 100644
index 0000000..ec22dcc
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Alipay/ScanShortcut.php
@@ -0,0 +1,32 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->miniPlugins();
+ }
+
+ protected function refundPlugins(): array
+ {
+ return $this->refundMiniPlugins();
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundMiniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryRefundPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Douyin/RefundShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Douyin/RefundShortcut.php
new file mode 100644
index 0000000..6bbac50
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Douyin/RefundShortcut.php
@@ -0,0 +1,52 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->miniPlugins();
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniRefundPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Jsb/QueryShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Jsb/QueryShortcut.php
new file mode 100644
index 0000000..83af51c
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Jsb/QueryShortcut.php
@@ -0,0 +1,30 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ webCancelPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ QrCodeCancelPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qraPosPlugins(): array
+ {
+ return [
+ QraStartPlugin::class,
+ QraPosCancelQueryPlugin::class,
+ QraAddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ QraVerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/H5Shortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/H5Shortcut.php
new file mode 100644
index 0000000..a1ac035
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/H5Shortcut.php
@@ -0,0 +1,30 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function preAuthPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ PosPreAuthPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qraPlugins(): array
+ {
+ return [
+ QraStartPlugin::class,
+ PayPlugin::class,
+ QraAddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ QraVerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/QueryShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/QueryShortcut.php
new file mode 100644
index 0000000..7f0b29e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/QueryShortcut.php
@@ -0,0 +1,97 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebQueryPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ QrCodeQueryPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qraPosPlugins(): array
+ {
+ return [
+ QraStartPlugin::class,
+ QraPosQueryPlugin::class,
+ QraAddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ QraVerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qraPosRefundPlugins(): array
+ {
+ return [
+ QraStartPlugin::class,
+ QraPosQueryRefundPlugin::class,
+ QraAddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ QraVerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/RefundShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/RefundShortcut.php
new file mode 100644
index 0000000..7539677
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/RefundShortcut.php
@@ -0,0 +1,83 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->webPlugins();
+ }
+
+ protected function webPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ WebRefundPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qrCodePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ QrCodeRefundPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function qraPosPlugins(): array
+ {
+ return [
+ QraStartPlugin::class,
+ QraPosRefundPlugin::class,
+ QraAddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ QraVerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/ScanShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/ScanShortcut.php
new file mode 100644
index 0000000..74cbf9e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/ScanShortcut.php
@@ -0,0 +1,89 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function preAuthPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanPreAuthPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function preOrderPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanPreOrderPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function feePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ScanFeePlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/WebShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/WebShortcut.php
new file mode 100644
index 0000000..5cae9a9
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Unipay/WebShortcut.php
@@ -0,0 +1,30 @@
+combinePlugins();
+ }
+
+ $method = Str::camel($params['_action'] ?? 'default').'Plugins';
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->jsapiPlugins();
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function H5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5ClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function jsapiPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ JsapiClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function nativePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ NativeClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function combinePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ CombineClosePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/H5Shortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/H5Shortcut.php
new file mode 100644
index 0000000..f8a95ec
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/H5Shortcut.php
@@ -0,0 +1,32 @@
+{$method}($params);
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function defaultPlugins(array $params): array
+ {
+ return $this->orderPlugins($params);
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function orderPlugins(array $params): array
+ {
+ return [
+ StartPlugin::class,
+ ContractOrderPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ $this->getInvoke($params),
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function contractPlugins(array $params): array
+ {
+ return match ($params['_type'] ?? 'default') {
+ 'mini' => [StartPlugin::class, MiniOnlyContractPlugin::class, AddPayloadSignaturePlugin::class],
+ default => throw new InvalidParamsException(Exception::PARAMS_WECHAT_PAPAY_TYPE_NOT_SUPPORTED, '参数异常: 微信扣关服务纯签约,当前传递的 `_type` 类型不支持')
+ };
+ }
+
+ protected function applyPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ ApplyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ /**
+ * @throws InvalidParamsException
+ */
+ protected function getInvoke(array $params): string
+ {
+ return match ($params['_type'] ?? 'default') {
+ 'app' => AppInvokePlugin::class,
+ 'mini' => MiniInvokePlugin::class,
+ default => throw new InvalidParamsException(Exception::PARAMS_WECHAT_PAPAY_TYPE_NOT_SUPPORTED, '参数异常: 微信扣关服务支付中签约,当前传递的 `_type` 类型不支持')
+ };
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/PosShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/PosShortcut.php
new file mode 100644
index 0000000..9681f20
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/PosShortcut.php
@@ -0,0 +1,32 @@
+combinePlugins();
+ }
+
+ $method = Str::camel($params['_action'] ?? 'default').'Plugins';
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->jsapiPlugins();
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function combinePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ CombineQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function h5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5QueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function jsapiPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ JsapiQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function nativePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ NativeQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundPlugins(): array
+ {
+ return $this->refundJsapiPlugins();
+ }
+
+ protected function refundAppPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppQueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundCombinePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ CombineQueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundH5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5QueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundJsapiPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ JsapiQueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundMiniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniQueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function refundNativePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ NativeQueryRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function transferPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ TransferQueryPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/RedpackShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/RedpackShortcut.php
new file mode 100644
index 0000000..3d5ed4e
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/RedpackShortcut.php
@@ -0,0 +1,32 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ protected function defaultPlugins(): array
+ {
+ return $this->jsapiPlugins();
+ }
+
+ protected function appPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ AppRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function combinePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ CombineRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function h5Plugins(): array
+ {
+ return [
+ StartPlugin::class,
+ H5RefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function jsapiPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ JsapiRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function miniPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ MiniRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ protected function nativePlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ NativeRefundPlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/ScanShortcut.php b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/ScanShortcut.php
new file mode 100644
index 0000000..9059e51
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Shortcut/Wechat/ScanShortcut.php
@@ -0,0 +1,32 @@
+{$method}();
+ }
+
+ throw new InvalidParamsException(Exception::PARAMS_SHORTCUT_ACTION_INVALID, "您所提供的 action 方法 [{$method}] 不支持,请参考文档或源码确认");
+ }
+
+ public function defaultPlugins(): array
+ {
+ return $this->transferPlugins();
+ }
+
+ public function transferPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ \Yansongda\Pay\Plugin\Wechat\V3\Marketing\Transfer\CreatePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+
+ public function mchTransferPlugins(): array
+ {
+ return [
+ StartPlugin::class,
+ CreatePlugin::class,
+ AddPayloadBodyPlugin::class,
+ AddPayloadSignaturePlugin::class,
+ AddRadarPlugin::class,
+ VerifySignaturePlugin::class,
+ ResponsePlugin::class,
+ ParserPlugin::class,
+ ];
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Pay/Traits/SupportServiceProviderTrait.php b/server/addons/epay/library/v3new/Yansongda/Pay/Traits/SupportServiceProviderTrait.php
new file mode 100644
index 0000000..14ae8ae
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Pay/Traits/SupportServiceProviderTrait.php
@@ -0,0 +1,35 @@
+getParams();
+ $config = get_provider_config('alipay', $params);
+ $serviceProviderId = $config['service_provider_id'] ?? null;
+
+ if (Pay::MODE_SERVICE !== ($config['mode'] ?? Pay::MODE_NORMAL)
+ || empty($serviceProviderId)) {
+ return;
+ }
+
+ $rocket->mergeParams([
+ 'extend_params' => array_merge($params['extend_params'] ?? [], ['sys_service_provider_id' => $serviceProviderId]),
+ ]);
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Arr.php b/server/addons/epay/library/v3new/Yansongda/Supports/Arr.php
new file mode 100644
index 0000000..a7a4022
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Arr.php
@@ -0,0 +1,550 @@
+all();
+ } elseif (!is_array($values)) {
+ continue;
+ }
+ $results[] = $values;
+ }
+
+ return array_merge([], ...$results);
+ }
+
+ public static function crossJoin(...$arrays): array
+ {
+ $results = [[]];
+ foreach ($arrays as $index => $array) {
+ $append = [];
+ foreach ($results as $product) {
+ foreach ($array as $item) {
+ $product[$index] = $item;
+ $append[] = $product;
+ }
+ }
+ $results = $append;
+ }
+
+ return $results;
+ }
+
+ public static function divide(array $array): array
+ {
+ return [array_keys($array), array_values($array)];
+ }
+
+ public static function dot(array $array, string $prepend = ''): array
+ {
+ $results = [];
+ foreach ($array as $key => $value) {
+ if (is_array($value) && !empty($value)) {
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+ } else {
+ $results[$prepend.$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ public static function except(array $array, array|string $keys): array
+ {
+ static::forget($array, $keys);
+
+ return $array;
+ }
+
+ public static function exists(array|ArrayAccess $array, int|string $key): bool
+ {
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+
+ return array_key_exists($key, $array);
+ }
+
+ public static function first(array $array, ?callable $callback = null, mixed $default = null): mixed
+ {
+ if (is_null($callback)) {
+ if (empty($array)) {
+ return $default;
+ }
+ foreach ($array as $item) {
+ return $item;
+ }
+ }
+ foreach ($array as $key => $value) {
+ if (call_user_func($callback, $value, $key)) {
+ return $value;
+ }
+ }
+
+ return $default;
+ }
+
+ public static function last(array $array, ?callable $callback = null, mixed $default = null): mixed
+ {
+ if (is_null($callback)) {
+ return empty($array) ? $default : end($array);
+ }
+
+ return static::first(array_reverse($array, true), $callback, $default);
+ }
+
+ public static function flatten(array $array, float|int $depth = INF): array
+ {
+ $result = [];
+ foreach ($array as $item) {
+ $item = $item instanceof Collection ? $item->all() : $item;
+ if (!is_array($item)) {
+ $result[] = $item;
+ } elseif (1 === $depth) {
+ $result = array_merge($result, array_values($item));
+ } else {
+ $result = array_merge($result, static::flatten($item, $depth - 1));
+ }
+ }
+
+ return $result;
+ }
+
+ public static function forget(array &$array, array|string $keys): void
+ {
+ $original = &$array;
+ $keys = (array) $keys;
+ if (0 === count($keys)) {
+ return;
+ }
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+
+ continue;
+ }
+ $parts = explode('.', $key);
+ // clean up before each pass
+ $array = &$original;
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+ if (isset($array[$part]) && is_array($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ public static function get(array|ArrayAccess $array, null|int|string $key = null, mixed $default = null): mixed
+ {
+ if (!static::accessible($array)) {
+ return $default;
+ }
+ if (is_null($key)) {
+ return $array;
+ }
+ if (static::exists($array, $key)) {
+ return $array[$key];
+ }
+ if (!is_string($key) || !str_contains($key, '.')) {
+ return $array[$key] ?? $default;
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($array) && static::exists($array, $segment)) {
+ $array = $array[$segment];
+ } else {
+ return $default;
+ }
+ }
+
+ return $array;
+ }
+
+ public static function has(array|ArrayAccess $array, null|array|string $keys): bool
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+ $keys = (array) $keys;
+ if (!$array) {
+ return false;
+ }
+ if ([] === $keys) {
+ return false;
+ }
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+ if (static::exists($array, $key)) {
+ continue;
+ }
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static function isAssoc(array $array): bool
+ {
+ $keys = array_keys($array);
+
+ return array_keys($keys) !== $keys;
+ }
+
+ public static function only(array $array, array|string $keys): array
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ public static function pluck(array $array, array|string $value, null|array|string $key = null): array
+ {
+ $results = [];
+
+ foreach ($array as $item) {
+ $itemValue = data_get($item, $value);
+ // If the key is "null", we will just append the value to the array and keep
+ // looping, Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+ $results[$itemKey] = $itemValue;
+ }
+ }
+
+ return $results;
+ }
+
+ public static function prepend(array $array, mixed $value, mixed $key = null): array
+ {
+ if (is_null($key)) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+
+ return $array;
+ }
+
+ public static function pull(array &$array, string $key, mixed $default = null): mixed
+ {
+ $value = static::get($array, $key, $default);
+ static::forget($array, $key);
+
+ return $value;
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ public static function random(array $array, int $number = 1): array
+ {
+ $count = count($array);
+ if ($number > $count) {
+ throw new InvalidArgumentException("You requested {$number} items, but there are only {$count} items available.");
+ }
+
+ if (0 === $number) {
+ return [];
+ }
+
+ $keys = array_rand($array, $number);
+ $results = [];
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ * If no key is given to the method, the entire array will be replaced.
+ */
+ public static function set(array &$array, null|int|string $key, mixed $value): array
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+ if (!is_string($key)) {
+ $array[$key] = $value;
+
+ return $array;
+ }
+ $keys = explode('.', $key);
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (!isset($array[$key]) || !is_array($array[$key])) {
+ $array[$key] = [];
+ }
+ $array = &$array[$key];
+ }
+ $array[array_shift($keys)] = $value;
+
+ return $array;
+ }
+
+ public static function shuffle(array $array, ?int $seed = null): array
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ srand($seed);
+ usort($array, function () {
+ return rand(-1, 1);
+ });
+ }
+
+ return $array;
+ }
+
+ public static function sort(array $array, callable $callback): array
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ $results[$key] = $callback($value);
+ }
+
+ return $results;
+ }
+
+ public static function sortRecursive(array $array): array
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = static::sortRecursive($value);
+ }
+ }
+ if (static::isAssoc($array)) {
+ ksort($array);
+ } else {
+ sort($array);
+ }
+
+ return $array;
+ }
+
+ public static function query(array $array, int $encodingType = PHP_QUERY_RFC1738): string
+ {
+ return http_build_query($array, '', '&', $encodingType);
+ }
+
+ public static function toString(array $array, string $separator = '&'): string
+ {
+ $result = '';
+
+ foreach ($array as $key => $value) {
+ $result .= $key.'='.$value.$separator;
+ }
+
+ return substr($result, 0, 0 - Str::length($separator));
+ }
+
+ public static function where(array $array, callable $callback): array
+ {
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+ }
+
+ public static function wrap(mixed $value): array
+ {
+ if (is_null($value)) {
+ return [];
+ }
+
+ return !is_array($value) ? [$value] : $value;
+ }
+
+ public static function wrapJson(string $json): ?array
+ {
+ $result = json_decode($json, true);
+
+ return is_array($result) ? $result : null;
+ }
+
+ public static function wrapXml(string $xml): array
+ {
+ if (empty($xml)) {
+ return [];
+ }
+
+ $data = json_decode(json_encode(
+ simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA),
+ JSON_UNESCAPED_UNICODE
+ ), true);
+
+ if (JSON_ERROR_NONE === json_last_error()) {
+ return $data;
+ }
+
+ return [];
+ }
+
+ /**
+ * @param bool $raw 是否原始解析,有些情况下,原始解析会更好
+ * @param bool $spaceToPlus 是否将空格转换为加号
+ */
+ public static function wrapQuery(string $query, bool $raw = false, bool $spaceToPlus = false): array
+ {
+ if (!$raw) {
+ parse_str($query, $data);
+
+ return $spaceToPlus ? self::querySpaceToPlus($data) : $data;
+ }
+
+ if (empty($query) || !Str::contains($query, '=')) {
+ return [];
+ }
+
+ $result = [];
+
+ foreach (explode('&', $query) as $item) {
+ $pos = strpos($item, '=');
+
+ $result[substr($item, 0, $pos)] = substr($item, $pos + 1);
+ }
+
+ return $result;
+ }
+
+ public static function querySpaceToPlus(array $data): array
+ {
+ foreach ($data as $key => $item) {
+ $data[$key] = is_array($item) ? self::querySpaceToPlus($item) : str_replace(' ', '+', $item);
+ }
+
+ return $data;
+ }
+
+ public static function unique(array $array): array
+ {
+ $result = [];
+ foreach ($array as $key => $item) {
+ if (is_array($item)) {
+ $result[$key] = self::unique($item);
+ } else {
+ $result[$key] = $item;
+ }
+ }
+
+ if (!self::isAssoc($result)) {
+ return array_unique($result);
+ }
+
+ return $result;
+ }
+
+ public static function merge(array $array1, array $array2, bool $unique = true): array
+ {
+ $isAssoc = static::isAssoc($array1 ?: $array2);
+ if ($isAssoc) {
+ foreach ($array2 as $key => $value) {
+ if (is_array($value)) {
+ $array1[$key] = static::merge($array1[$key] ?? [], $value, $unique);
+ } else {
+ $array1[$key] = $value;
+ }
+ }
+ } else {
+ foreach ($array2 as $value) {
+ if ($unique && in_array($value, $array1, true)) {
+ continue;
+ }
+ $array1[] = $value;
+ }
+
+ $array1 = array_values($array1);
+ }
+
+ return $array1;
+ }
+
+ public static function encoding(array $array, string $to_encoding, string $from_encoding = 'gb2312'): array
+ {
+ $encoded = [];
+
+ foreach ($array as $key => $value) {
+ $encoded[$key] = is_array($value) ? self::encoding($value, $to_encoding, $from_encoding) :
+ mb_convert_encoding($value, $to_encoding, $from_encoding);
+ }
+
+ return $encoded;
+ }
+
+ public static function camelCaseKey(mixed $data): mixed
+ {
+ if (!self::accessible($data)
+ && !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $result = [];
+
+ foreach ((is_object($data) ? $data->toArray() : $data) as $key => $value) {
+ $result[is_string($key) ? Str::camel($key) : $key] = self::camelCaseKey($value);
+ }
+
+ return $result;
+ }
+
+ public static function snakeCaseKey(mixed $data): mixed
+ {
+ if (!self::accessible($data)
+ && !(is_object($data) && method_exists($data, 'toArray'))) {
+ return $data;
+ }
+
+ $result = [];
+
+ foreach ((is_object($data) ? $data->toArray() : $data) as $key => $value) {
+ $result[is_string($key) ? Str::snake($key) : $key] = self::snakeCaseKey($value);
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Collection.php b/server/addons/epay/library/v3new/Yansongda/Supports/Collection.php
new file mode 100644
index 0000000..2c3d349
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Collection.php
@@ -0,0 +1,361 @@
+getArrayableItems($items) as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ public static function wrap(mixed $value): self
+ {
+ return $value instanceof self ? new static($value) : new static(Arr::wrap($value));
+ }
+
+ public static function wrapJson(string $json): self
+ {
+ return new static(Arr::wrapJson($json));
+ }
+
+ public static function wrapXml(string $xml): self
+ {
+ return new static(Arr::wrapXml($xml));
+ }
+
+ public static function wrapQuery(string $query, bool $raw = false, bool $spaceToPlus = false): self
+ {
+ return new static(Arr::wrapQuery($query, $raw, $spaceToPlus));
+ }
+
+ public static function unwrap(array|Collection $value): array
+ {
+ return $value instanceof self ? $value->all() : $value;
+ }
+
+ public function all(): array
+ {
+ return $this->items;
+ }
+
+ public function only(array $keys): array
+ {
+ $return = [];
+
+ foreach ($keys as $key) {
+ $value = $this->get($key);
+
+ if (!is_null($value)) {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ public function except(mixed $keys): self
+ {
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ public function filter(?callable $callback = null): self
+ {
+ if ($callback) {
+ return new static(Arr::where($this->items, $callback));
+ }
+
+ return new static(array_filter($this->items));
+ }
+
+ public function merge(mixed $items): self
+ {
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
+ }
+
+ public function has(int|string $key): bool
+ {
+ return !is_null(Arr::get($this->items, $key));
+ }
+
+ public function first(): mixed
+ {
+ return reset($this->items);
+ }
+
+ public function last(): mixed
+ {
+ $end = end($this->items);
+
+ reset($this->items);
+
+ return $end;
+ }
+
+ public function add(null|int|string $key, mixed $value): void
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ public function set(null|int|string $key, mixed $value): void
+ {
+ Arr::set($this->items, $key, $value);
+ }
+
+ public function get(null|int|string $key = null, mixed $default = null): mixed
+ {
+ return Arr::get($this->items, $key, $default);
+ }
+
+ public function forget(int|string $key): void
+ {
+ Arr::forget($this->items, $key);
+ }
+
+ public function flatten(float|int $depth = INF): self
+ {
+ return new static(Arr::flatten($this->items, $depth));
+ }
+
+ public function map(callable $callback): self
+ {
+ $keys = array_keys($this->items);
+ $items = array_map($callback, $this->items, $keys);
+
+ return new static(array_combine($keys, $items));
+ }
+
+ public function pop(): mixed
+ {
+ return array_pop($this->items);
+ }
+
+ public function prepend(mixed $value, mixed $key = null): self
+ {
+ $this->items = Arr::prepend($this->items, $value, $key);
+
+ return $this;
+ }
+
+ public function push(mixed $value): self
+ {
+ $this->offsetSet(null, $value);
+
+ return $this;
+ }
+
+ public function pull(mixed $key, mixed $default = null): mixed
+ {
+ return Arr::pull($this->items, $key, $default);
+ }
+
+ public function put(mixed $key, mixed $value): self
+ {
+ $this->offsetSet($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ public function random(?int $number = null): self
+ {
+ return new static(Arr::random($this->items, $number ?? 1));
+ }
+
+ public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ return array_reduce($this->items, $callback, $initial);
+ }
+
+ public function values(): self
+ {
+ return new static(array_values($this->items));
+ }
+
+ public function every(callable|string $key): bool
+ {
+ $callback = $this->valueRetriever($key);
+
+ foreach ($this->items as $k => $v) {
+ if (!$callback($v, $k)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function chunk(int $size): self
+ {
+ if ($size <= 0) {
+ return new static();
+ }
+ $chunks = [];
+ foreach (array_chunk($this->items, $size, true) as $chunk) {
+ $chunks[] = new static($chunk);
+ }
+
+ return new static($chunks);
+ }
+
+ public function sort(?callable $callback = null): self
+ {
+ $items = $this->items;
+ $callback ? uasort($items, $callback) : asort($items);
+
+ return new static($items);
+ }
+
+ public function sortBy(callable|string $callback, int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ $results = [];
+ $callback = $this->valueRetriever($callback);
+ // First we will loop through the items and get the comparator from a callback
+ // function which we were given. Then, we will sort the returned values
+ // and grab the corresponding values for the sorted keys from this array.
+ foreach ($this->items as $key => $value) {
+ $results[$key] = $callback($value, $key);
+ }
+ $descending ? arsort($results, $options) : asort($results, $options);
+ // Once we have sorted all the keys in the array, we will loop through them
+ // and grab the corresponding model, so we can set the underlying items list
+ // to the sorted version. Then we'll just return the collection instance.
+ foreach (array_keys($results) as $key) {
+ $results[$key] = $this->items[$key];
+ }
+
+ return new static($results);
+ }
+
+ public function sortByDesc(callable|string $callback, int $options = SORT_REGULAR): self
+ {
+ return $this->sortBy($callback, $options, true);
+ }
+
+ public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): self
+ {
+ $items = $this->items;
+ $descending ? krsort($items, $options) : ksort($items, $options);
+
+ return new static($items);
+ }
+
+ public function sortKeysDesc(int $options = SORT_REGULAR): self
+ {
+ return $this->sortKeys($options, true);
+ }
+
+ public function query(int $encodingType = PHP_QUERY_RFC1738): string
+ {
+ return Arr::query($this->all(), $encodingType);
+ }
+
+ public function toString(string $separator = '&'): string
+ {
+ return Arr::toString($this->all(), $separator);
+ }
+
+ public function toArray(): array
+ {
+ return $this->all();
+ }
+
+ public function toXml(): string
+ {
+ $xml = '';
+
+ foreach ($this->all() as $key => $val) {
+ $xml .= is_numeric($val) ? '<'.$key.'>'.$val.''.$key.'>' :
+ '<'.$key.'>'.$key.'>';
+ }
+
+ $xml .= ' ';
+
+ return $xml;
+ }
+
+ public function getIterator(): Traversable
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ public function count(): int
+ {
+ return count($this->items);
+ }
+
+ public function isEmpty(): bool
+ {
+ return empty($this->items);
+ }
+
+ public function isNotEmpty(): bool
+ {
+ return !$this->isEmpty();
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ if ($this->offsetExists($offset)) {
+ $this->forget($offset);
+ }
+ }
+
+ protected function useAsCallable(mixed $value): bool
+ {
+ return !is_string($value) && is_callable($value);
+ }
+
+ protected function valueRetriever(mixed $value): callable
+ {
+ if ($this->useAsCallable($value)) {
+ return $value;
+ }
+
+ return function ($item) use ($value) {
+ return data_get($item, $value);
+ };
+ }
+
+ protected function getArrayableItems(mixed $items): array
+ {
+ if (is_array($items)) {
+ return $items;
+ }
+
+ if ($items instanceof self) {
+ return $items->all();
+ }
+
+ if ($items instanceof JsonSerializable) {
+ return $items->jsonSerialize();
+ }
+
+ return (array) $items;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Config.php b/server/addons/epay/library/v3new/Yansongda/Supports/Config.php
new file mode 100644
index 0000000..2788d05
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Config.php
@@ -0,0 +1,7 @@
+all();
+ } elseif (!is_array($target)) {
+ return value($default);
+ }
+ $result = [];
+ foreach ($target as $item) {
+ $result[] = data_get($item, $key);
+ }
+
+ return in_array('*', $key) ? Arr::collapse($result) : $result;
+ }
+ if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+ $target = $target[$segment];
+ } elseif (is_object($target) && isset($target->{$segment})) {
+ $target = $target->{$segment};
+ } else {
+ return value($default);
+ }
+ }
+
+ return $target;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Pipeline.php b/server/addons/epay/library/v3new/Yansongda/Supports/Pipeline.php
new file mode 100644
index 0000000..dcaa29f
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Pipeline.php
@@ -0,0 +1,115 @@
+container = $container;
+ }
+
+ public function send(mixed $passable): self
+ {
+ $this->passable = $passable;
+
+ return $this;
+ }
+
+ public function through(mixed $pipes): self
+ {
+ $this->pipes = is_array($pipes) ? $pipes : func_get_args();
+
+ return $this;
+ }
+
+ /**
+ * Set the method to call on the pipes.
+ */
+ public function via(string $method): self
+ {
+ $this->method = $method;
+
+ return $this;
+ }
+
+ public function then(Closure $destination): mixed
+ {
+ $pipeline = array_reduce(array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination));
+
+ return $pipeline($this->passable);
+ }
+
+ protected function prepareDestination(Closure $destination): Closure
+ {
+ return static function ($passable) use ($destination) {
+ return $destination($passable);
+ };
+ }
+
+ protected function carry(): Closure
+ {
+ return function ($stack, $pipe) {
+ return function ($passable) use ($stack, $pipe) {
+ if (is_callable($pipe)) {
+ // If the pipe is an instance of a Closure, we will just call it directly, but
+ // otherwise we'll resolve the pipes out of the container and call it with
+ // the appropriate method and arguments, returning the results back out.
+ return $pipe($passable, $stack);
+ }
+ if (!is_object($pipe)) {
+ [$name, $parameters] = $this->parsePipeString($pipe);
+
+ // If the pipe is a string we will parse the string and resolve the class out
+ // of the dependency injection container. We can then build a callable and
+ // execute the pipe function giving in the parameters that are required.
+ $pipe = $this->container->get($name);
+
+ $parameters = array_merge([$passable, $stack], $parameters);
+ } else {
+ // If the pipe is already an object we'll just make a callable and pass it to
+ // the pipe as-is. There is no need to do any extra parsing and formatting
+ // since the object we're given was already a fully instantiated object.
+ $parameters = [$passable, $stack];
+ }
+
+ $carry = method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters);
+
+ return $this->handleCarry($carry);
+ };
+ };
+ }
+
+ protected function parsePipeString(string $pipe): array
+ {
+ [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
+
+ if (is_string($parameters)) {
+ $parameters = explode(',', $parameters);
+ }
+
+ return [$name, $parameters];
+ }
+
+ protected function handleCarry(mixed $carry): mixed
+ {
+ return $carry;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Str.php b/server/addons/epay/library/v3new/Yansongda/Supports/Str.php
new file mode 100644
index 0000000..2abad72
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Str.php
@@ -0,0 +1,455 @@
+ $val) {
+ $value = str_replace($val, $key, $value);
+ }
+
+ return preg_replace('/[^\x20-\x7E]/u', '', $value);
+ }
+
+ public static function before(string $subject, string $search): string
+ {
+ return '' === $search ? $subject : explode($search, $subject)[0];
+ }
+
+ public static function camel(string $value): string
+ {
+ return lcfirst(static::studly($value));
+ }
+
+ public static function contains(string $haystack, array|string $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if (str_contains($haystack, $needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function endsWith(string $haystack, array|string $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if (str_ends_with($haystack, $needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function finish(string $value, string $cap): string
+ {
+ $quoted = preg_quote($cap, '/');
+
+ return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
+ }
+
+ public static function is(array|string $pattern, string $value): bool
+ {
+ $patterns = Arr::wrap($pattern);
+
+ if (empty($patterns)) {
+ return false;
+ }
+
+ foreach ($patterns as $pattern) {
+ // If the given value is an exact match we can of course return true right
+ // from the beginning. Otherwise, we will translate asterisks and do an
+ // actual pattern match against the two strings to see if they match.
+ if ($pattern == $value) {
+ return true;
+ }
+
+ $pattern = preg_quote($pattern, '#');
+
+ // Asterisks are translated into zero-or-more regular expression wildcards
+ // to make it convenient to check if the strings starts with the given
+ // pattern such as "library/*", making any string check convenient.
+ $pattern = str_replace('\*', '.*', $pattern);
+
+ if (1 === preg_match('#^'.$pattern.'\z#u', $value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function kebab(string $value): string
+ {
+ return static::snake($value, '-');
+ }
+
+ public static function length(string $value, ?string $encoding = null): int
+ {
+ if (null !== $encoding) {
+ return mb_strlen($value, $encoding);
+ }
+
+ return mb_strlen($value);
+ }
+
+ public static function limit(string $value, int $limit = 100, string $end = '...'): string
+ {
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
+ return $value;
+ }
+
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+ }
+
+ public static function lower(string $value): string
+ {
+ return mb_strtolower($value, 'UTF-8');
+ }
+
+ public static function words(string $value, int $words = 100, string $end = '...'): string
+ {
+ preg_match('/^\s*+\S++\s*+{1,'.$words.'}/u', $value, $matches);
+
+ if (!isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+ return $value;
+ }
+
+ return rtrim($matches[0]).$end;
+ }
+
+ public static function parseCallback(string $callback, ?string $default = null): array
+ {
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+ }
+
+ public static function random(int $length = 16): string
+ {
+ $string = '';
+
+ while (($len = strlen($string)) < $length) {
+ $size = $length - $len;
+
+ $bytes = random_bytes($size);
+
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+ }
+
+ return $string;
+ }
+
+ public static function uuidV4(): string
+ {
+ return sprintf(
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+ // 32 bits for "time_low"
+ mt_rand(0, 0xFFFF),
+ mt_rand(0, 0xFFFF),
+
+ // 16 bits for "time_mid"
+ mt_rand(0, 0xFFFF),
+
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand(0, 0x0FFF) | 0x4000,
+
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand(0, 0x3FFF) | 0x8000,
+
+ // 48 bits for "node"
+ mt_rand(0, 0xFFFF),
+ mt_rand(0, 0xFFFF),
+ mt_rand(0, 0xFFFF)
+ );
+ }
+
+ public static function replaceArray(string $search, array $replace, string $subject): string
+ {
+ foreach ($replace as $value) {
+ $subject = static::replaceFirst($search, $value, $subject);
+ }
+
+ return $subject;
+ }
+
+ public static function replaceFirst(string $search, string $replace, string $subject): string
+ {
+ if ('' == $search) {
+ return $subject;
+ }
+
+ $position = strpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ public static function replaceLast(string $search, string $replace, string $subject): string
+ {
+ $position = strrpos($subject, $search);
+
+ if (false !== $position) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ public static function start(string $value, string $prefix): string
+ {
+ $quoted = preg_quote($prefix, '/');
+
+ return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+ }
+
+ public static function upper(string $value): string
+ {
+ return mb_strtoupper($value, 'UTF-8');
+ }
+
+ public static function title(string $value): string
+ {
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ public static function slug(string $title, string $separator = '-', string $language = 'en'): string
+ {
+ $title = static::ascii($title, $language);
+
+ // Convert all dashes/underscores into separator
+ $flip = '-' == $separator ? '_' : '-';
+
+ $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+ // Replace @ with the word 'at'
+ $title = str_replace('@', $separator.'at'.$separator, $title);
+
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
+ $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+ return trim($title, $separator);
+ }
+
+ public static function snake(string $value, string $delimiter = '_'): string
+ {
+ if (!ctype_lower($value)) {
+ $value = preg_replace('/\s+/u', '', ucwords($value));
+
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+ }
+
+ return $value;
+ }
+
+ public static function startsWith(int|string $haystack, array|string $needles): bool
+ {
+ foreach ((array) $needles as $needle) {
+ if (str_starts_with(strval($haystack), $needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function studly(string $value, string $gap = ''): string
+ {
+ $value = ucwords(str_replace(['-', '_'], ' ', $value));
+
+ return str_replace(' ', $gap, $value);
+ }
+
+ public static function substr(string $string, int $start, ?int $length = null): string
+ {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ public static function ucfirst(string $string): string
+ {
+ return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+ }
+
+ public static function encoding(string $string, string $to = 'utf-8', string $from = 'gb2312'): string
+ {
+ return mb_convert_encoding($string, $to, $from);
+ }
+
+ /**
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function charsArray(): array
+ {
+ static $charsArray;
+
+ if (isset($charsArray)) {
+ return $charsArray;
+ }
+
+ return $charsArray = [
+ '0' => ['°', '₀', '۰', '0'],
+ '1' => ['¹', '₁', '۱', '1'],
+ '2' => ['²', '₂', '۲', '2'],
+ '3' => ['³', '₃', '۳', '3'],
+ '4' => ['⁴', '₄', '۴', '٤', '4'],
+ '5' => ['⁵', '₅', '۵', '٥', '5'],
+ '6' => ['⁶', '₆', '۶', '٦', '6'],
+ '7' => ['⁷', '₇', '۷', '7'],
+ '8' => ['⁸', '₈', '۸', '8'],
+ '9' => ['⁹', '₉', '۹', '9'],
+ 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'],
+ 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
+ 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
+ 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
+ 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'],
+ 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
+ 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
+ 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
+ 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i'],
+ 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
+ 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
+ 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
+ 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
+ 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
+ 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö'],
+ 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
+ 'q' => ['ყ', 'q'],
+ 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
+ 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
+ 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
+ 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü'],
+ 'v' => ['в', 'ვ', 'ϐ', 'v'],
+ 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
+ 'x' => ['χ', 'ξ', 'x'],
+ 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
+ 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
+ 'aa' => ['ع', 'आ', 'آ'],
+ 'ae' => ['æ', 'ǽ'],
+ 'ai' => ['ऐ'],
+ 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
+ 'dj' => ['ђ', 'đ'],
+ 'dz' => ['џ', 'ძ'],
+ 'ei' => ['ऍ'],
+ 'gh' => ['غ', 'ღ'],
+ 'ii' => ['ई'],
+ 'ij' => ['ij'],
+ 'kh' => ['х', 'خ', 'ხ'],
+ 'lj' => ['љ'],
+ 'nj' => ['њ'],
+ 'oe' => ['ö', 'œ', 'ؤ'],
+ 'oi' => ['ऑ'],
+ 'oii' => ['ऒ'],
+ 'ps' => ['ψ'],
+ 'sh' => ['ш', 'შ', 'ش'],
+ 'shch' => ['щ'],
+ 'ss' => ['ß'],
+ 'sx' => ['ŝ'],
+ 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
+ 'ts' => ['ц', 'ც', 'წ'],
+ 'ue' => ['ü'],
+ 'uu' => ['ऊ'],
+ 'ya' => ['я'],
+ 'yu' => ['ю'],
+ 'zh' => ['ж', 'ჟ', 'ژ'],
+ '(c)' => ['©'],
+ 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'],
+ 'B' => ['Б', 'Β', 'ब', 'B'],
+ 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
+ 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
+ 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E'],
+ 'F' => ['Ф', 'Φ', 'F'],
+ 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
+ 'H' => ['Η', 'Ή', 'Ħ', 'H'],
+ 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I'],
+ 'J' => ['J'],
+ 'K' => ['К', 'Κ', 'K'],
+ 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
+ 'M' => ['М', 'Μ', 'M'],
+ 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
+ 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'],
+ 'P' => ['П', 'Π', 'P'],
+ 'Q' => ['Q'],
+ 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
+ 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
+ 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
+ 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'],
+ 'V' => ['В', 'V'],
+ 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
+ 'X' => ['Χ', 'Ξ', 'X'],
+ 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
+ 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
+ 'AE' => ['Æ', 'Ǽ'],
+ 'Ch' => ['Ч'],
+ 'Dj' => ['Ђ'],
+ 'Dz' => ['Џ'],
+ 'Gx' => ['Ĝ'],
+ 'Hx' => ['Ĥ'],
+ 'Ij' => ['IJ'],
+ 'Jx' => ['Ĵ'],
+ 'Kh' => ['Х'],
+ 'Lj' => ['Љ'],
+ 'Nj' => ['Њ'],
+ 'Oe' => ['Œ'],
+ 'Ps' => ['Ψ'],
+ 'Sh' => ['Ш'],
+ 'Shch' => ['Щ'],
+ 'Ss' => ['ẞ'],
+ 'Th' => ['Þ'],
+ 'Ts' => ['Ц'],
+ 'Ya' => ['Я'],
+ 'Yu' => ['Ю'],
+ 'Zh' => ['Ж'],
+ ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0"],
+ ];
+ }
+
+ /**
+ * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
+ */
+ protected static function languageSpecificCharsArray(string $language): ?array
+ {
+ static $languageSpecific;
+ if (!isset($languageSpecific)) {
+ $languageSpecific = [
+ 'bg' => [
+ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
+ ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
+ ],
+ 'de' => [
+ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
+ ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
+ ],
+ ];
+ }
+
+ return $languageSpecific[$language] ?? null;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Accessable.php b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Accessable.php
new file mode 100644
index 0000000..5576dd1
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Accessable.php
@@ -0,0 +1,73 @@
+get($key);
+ }
+
+ public function __isset(string $key): bool
+ {
+ return !is_null($this->get($key));
+ }
+
+ public function __unset(string $key): void
+ {
+ $this->offsetUnset($key);
+ }
+
+ public function __set(string $key, mixed $value): void
+ {
+ $this->set($key, $value);
+ }
+
+ public function get(?string $key = null, mixed $default = null): mixed
+ {
+ if (is_null($key)) {
+ return method_exists($this, 'toArray') ? $this->toArray() : $default;
+ }
+
+ $method = 'get'.Str::studly($key);
+
+ if (method_exists($this, $method)) {
+ return $this->{$method}();
+ }
+
+ return $default;
+ }
+
+ public function set(string $key, mixed $value): self
+ {
+ $method = 'set'.Str::studly($key);
+
+ if (method_exists($this, $method)) {
+ $this->{$method}($value);
+ }
+
+ return $this;
+ }
+
+ public function offsetExists(mixed $offset): bool
+ {
+ return !is_null($this->get($offset));
+ }
+
+ public function offsetGet(mixed $offset): mixed
+ {
+ return $this->get($offset);
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ $this->set($offset, $value);
+ }
+
+ public function offsetUnset(mixed $offset): void {}
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Arrayable.php b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Arrayable.php
new file mode 100644
index 0000000..0e0d3dd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Arrayable.php
@@ -0,0 +1,25 @@
+getProperties() as $item) {
+ $k = $item->getName();
+ $method = 'get'.Str::studly($k);
+
+ $result[Str::snake($k)] = method_exists($this, $method) ? $this->{$method}() : $this->{$k};
+ }
+
+ return $result;
+ }
+}
diff --git a/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Serializable.php b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Serializable.php
new file mode 100644
index 0000000..9735dcd
--- /dev/null
+++ b/server/addons/epay/library/v3new/Yansongda/Supports/Traits/Serializable.php
@@ -0,0 +1,58 @@
+toArray();
+ }
+
+ return [];
+ }
+
+ public function __unserialize(array $data): void
+ {
+ $this->unserializeArray($data);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ public function serialize(): ?string
+ {
+ return serialize($this);
+ }
+
+ public function unserialize(string $data): void
+ {
+ unserialize($data);
+ }
+
+ public function toJson(int $option = JSON_UNESCAPED_UNICODE): string
+ {
+ return json_encode($this->__serialize(), $option);
+ }
+
+ public function jsonSerialize(): array
+ {
+ return $this->__serialize();
+ }
+
+ public function unserializeArray(array $data): self
+ {
+ foreach ($data as $key => $item) {
+ if (method_exists($this, 'set')) {
+ $this->set(strval($key), $item);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/server/addons/epay/library/v3new/version.txt b/server/addons/epay/library/v3new/version.txt
new file mode 100644
index 0000000..9de88e1
--- /dev/null
+++ b/server/addons/epay/library/v3new/version.txt
@@ -0,0 +1 @@
+v3.7.14
\ No newline at end of file
diff --git a/server/addons/epay/licenses/yansongda-pay.txt b/server/addons/epay/licenses/yansongda-pay.txt
new file mode 100644
index 0000000..6ceb153
--- /dev/null
+++ b/server/addons/epay/licenses/yansongda-pay.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 yansongda
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/server/addons/epay/view/api/alipay.html b/server/addons/epay/view/api/alipay.html
new file mode 100644
index 0000000..9edf12d
--- /dev/null
+++ b/server/addons/epay/view/api/alipay.html
@@ -0,0 +1,47 @@
+
+
+ 支付宝支付
+
+ 请在 60 秒内完成支付
+
+
+
+
+
+
+
+
+
+
+
订单标题:{$orderData.title}
+
订单编号:{$orderData.orderid}
+
订单价格:¥{$orderData.amount} 元
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server/addons/epay/view/api/wechat.html b/server/addons/epay/view/api/wechat.html
new file mode 100644
index 0000000..07af957
--- /dev/null
+++ b/server/addons/epay/view/api/wechat.html
@@ -0,0 +1,90 @@
+{if $type=='jsapi'}
+
+
+
+ 正在发起微信支付
+ 如果页面未自动跳转
+
+
+
+
+
+
+
+{elseif $type=='pc' /}
+
+
+ 微信支付
+
+ 请在 60 秒内完成支付
+
+
+
+
+
+
+
+
+
+
+
订单标题:{$orderData.title}
+
订单编号:{$orderData.orderid}
+
订单价格:¥{$orderData.amount} 元
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{/if}
diff --git a/server/addons/epay/view/index/index.html b/server/addons/epay/view/index/index.html
new file mode 100644
index 0000000..e70024a
--- /dev/null
+++ b/server/addons/epay/view/index/index.html
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
开发工作
+
+
+
在你的PHP代码中调用相关代码进行支付,请参考控制器代码
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server/addons/epay/view/layout/default.html b/server/addons/epay/view/layout/default.html
new file mode 100644
index 0000000..03c9404
--- /dev/null
+++ b/server/addons/epay/view/layout/default.html
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+ {$title} - {$site.name}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{__CONTENT__}
+
+
+
+
+
+
+
+
Copyright © {$site.name} 2017-{:date("Y")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server/application/admin/controller/Master.php b/server/application/admin/controller/Master.php
index 854d636..21beeb0 100644
--- a/server/application/admin/controller/Master.php
+++ b/server/application/admin/controller/Master.php
@@ -26,16 +26,6 @@ class Master extends Backend
$this->view->assign("verifyStatusList", $this->model->getVerifyStatusList());
}
-
-
- /**
- * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
- * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
- * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
- */
-
-
-
protected $relationSearch = true;
public function index()
@@ -56,4 +46,43 @@ class Master extends Backend
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
+
+ /**
+ * 师傅全景档案画像
+ */
+ public function detail($ids = null)
+ {
+ $row = $this->model->with(['user'])->find($ids);
+ if (!$row) {
+ $this->error(__('No Results were found'));
+ }
+
+ // 1. 最近接单记录
+ $recentOrders = \think\Db::name('order')
+ ->where('master_id', $row['id'])
+ ->order('id desc')
+ ->limit(6)
+ ->select();
+
+ // 2. 提现流水
+ $recentWithdraws = \think\Db::name('withdraw')
+ ->where('master_id', $row['id'])
+ ->order('id desc')
+ ->limit(5)
+ ->select();
+
+ // 3. 服务好评记录
+ $recentReviews = \think\Db::name('review')
+ ->where('master_id', $row['id'])
+ ->order('id desc')
+ ->limit(5)
+ ->select();
+
+ $this->view->assign('row', $row);
+ $this->view->assign('recentOrders', $recentOrders);
+ $this->view->assign('recentWithdraws', $recentWithdraws);
+ $this->view->assign('recentReviews', $recentReviews);
+
+ return $this->view->fetch();
+ }
}
diff --git a/server/application/admin/controller/Merchant.php b/server/application/admin/controller/Merchant.php
index 296e01e..65167be 100644
--- a/server/application/admin/controller/Merchant.php
+++ b/server/application/admin/controller/Merchant.php
@@ -26,16 +26,6 @@ class Merchant extends Backend
$this->view->assign("verifyStatusList", $this->model->getVerifyStatusList());
}
-
-
- /**
- * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
- * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
- * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
- */
-
-
-
protected $relationSearch = true;
public function index()
@@ -56,4 +46,27 @@ class Merchant extends Backend
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
+
+ /**
+ * 商户企业全景资质
+ */
+ public function detail($ids = null)
+ {
+ $row = $this->model->with(['user'])->find($ids);
+ if (!$row) {
+ $this->error(__('No Results were found'));
+ }
+
+ // 查询商户派发的工单记录
+ $recentOrders = \think\Db::name('order')
+ ->where('user_id', $row['user_id'])
+ ->order('id desc')
+ ->limit(6)
+ ->select();
+
+ $this->view->assign('row', $row);
+ $this->view->assign('recentOrders', $recentOrders);
+
+ return $this->view->fetch();
+ }
}
diff --git a/server/application/admin/controller/Order.php b/server/application/admin/controller/Order.php
index 32c2791..9de61dc 100644
--- a/server/application/admin/controller/Order.php
+++ b/server/application/admin/controller/Order.php
@@ -31,16 +31,6 @@ class Order extends Backend
$this->view->assign("urgentList", $this->model->getUrgentList());
}
-
-
- /**
- * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
- * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
- * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
- */
-
-
-
protected $relationSearch = true;
public function index()
@@ -61,4 +51,49 @@ class Order extends Backend
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
+
+ /**
+ * 工单全景详情
+ */
+ public function detail($ids = null)
+ {
+ $row = $this->model->with(['user', 'master'])->find($ids);
+ if (!$row) {
+ $this->error(__('No Results were found'));
+ }
+
+ // 1. 多商品/多工项明细
+ $goodsList = \think\Db::name('order_goods')->where('order_id', $row['id'])->select();
+
+ // 2. 现场施工取证图片 (区分报损、过程、完工)
+ $mediaList = \think\Db::name('order_media')->where('order_id', $row['id'])->order('id asc')->select();
+
+ // 3. 履约轨迹日志
+ $logList = \think\Db::name('order_log')->where('order_id', $row['id'])->order('createtime asc')->select();
+
+ // 4. 师傅报价列表
+ $quoteList = \think\Db::name('order_quote')
+ ->alias('q')
+ ->join('fa_master m', 'q.master_id = m.id', 'LEFT')
+ ->field('q.*, m.realname as master_name, m.mobile as master_mobile, m.score as master_score')
+ ->where('q.order_id', $row['id'])
+ ->order('q.id desc')
+ ->select();
+
+ // 5. 客户评价与回复
+ $review = \think\Db::name('review')->where('order_id', $row['id'])->find();
+
+ // 6. 财务结算记录
+ $settle = \think\Db::name('settle')->where('order_id', $row['id'])->find();
+
+ $this->view->assign('row', $row);
+ $this->view->assign('goodsList', $goodsList);
+ $this->view->assign('mediaList', $mediaList);
+ $this->view->assign('logList', $logList);
+ $this->view->assign('quoteList', $quoteList);
+ $this->view->assign('review', $review);
+ $this->view->assign('settle', $settle);
+
+ return $this->view->fetch();
+ }
}
diff --git a/server/application/admin/lang/zh-cn.php b/server/application/admin/lang/zh-cn.php
index 867fa42..98a42ab 100755
--- a/server/application/admin/lang/zh-cn.php
+++ b/server/application/admin/lang/zh-cn.php
@@ -1,7 +1,7 @@
'会员ID',
+ 'User id' => '关联用户',
'Username' => '用户名',
'Nickname' => '昵称',
'Mobile' => '手机',
@@ -192,9 +192,9 @@ return [
'Admin log' => '管理员日志',
'Group' => '角色组',
'Rule' => '菜单规则',
- 'User' => '会员管理',
- 'User group' => '会员分组',
- 'User rule' => '会员规则',
+ 'User' => '用户管理',
+ 'User group' => '用户分组',
+ 'User rule' => '用户规则',
'Select attachment' => '选择附件',
'Update profile' => '更新个人信息',
'Local install' => '本地安装',
@@ -218,9 +218,9 @@ return [
// 前台菜单
'Frontend' => '前台',
'API Interface' => 'API接口',
- 'User Module' => '会员模块',
+ 'User Module' => '用户模块',
'Register' => '注册',
- 'User Center' => '会员中心',
+ 'User Center' => '用户中心',
// ==================== 业务后台字段与状态中文 ====================
'Accept_rate' => '接单率',
@@ -244,7 +244,7 @@ return [
'Base_price' => '基础价',
'Cancel_reason' => '取消原因',
'Cash' => '现金券',
- 'Cat_id' => '类目ID',
+ 'Cat_id' => '所属类目',
'Cert_images' => '资质图片',
'Channel' => '提现渠道',
'Commission' => '佣金',
@@ -252,7 +252,7 @@ return [
'Contact' => '联系人',
'Contact_time' => '联系客户时间',
'Control panel' => '控制台',
- 'Coupon_id' => '优惠券ID',
+ 'Coupon_id' => '优惠券',
'Date_text' => '日期文案',
'Deduct' => '扣款',
'Discount' => '折扣券',
@@ -277,8 +277,8 @@ return [
'Goods_num' => '商品数量',
'Handling' => '处理中',
'Has_elevator' => '有无电梯',
- 'Idcard_back' => '身份证反面',
- 'Idcard_front' => '身份证正面',
+ 'Idcard_back' => '身份证国徽面',
+ 'Idcard_front' => '身份证人像面',
'Idcard_no' => '身份证号',
'Install Successed' => '安装成功',
'Install now' => '立即安装',
@@ -301,9 +301,9 @@ return [
'Link' => '跳转链接',
'Lng' => '经度',
'Master' => '师傅',
- 'Master_id' => '师傅ID',
+ 'Master_id' => '接单师傅',
'Max_price' => '最高价',
- 'Merchant_id' => '商家ID',
+ 'Merchant_id' => '所属商户',
'Min_amount' => '最低消费',
'Misjudge' => '误判',
'Monthly' => '月结',
@@ -325,7 +325,7 @@ return [
'Order' => '订单',
'Order_amount' => '订单金额',
'Order_count' => '订单数',
- 'Order_id' => '订单ID',
+ 'Order_id' => '关联订单',
'Order_no' => '订单号',
'Other' => '其他',
'PDO is not currently installed and cannot be installed' => '未安装 PDO 扩展,无法继续安装',
@@ -345,7 +345,7 @@ return [
'Quote' => '师傅报价',
'Range' => '按面积计价',
'Realname' => '真实姓名',
- 'Receive_id' => '接收人ID',
+ 'Receive_id' => '接收人',
'Receive_type' => '接收类型',
'Received' => '已领取',
'Refund_amount' => '退款金额',
@@ -356,7 +356,7 @@ return [
'Review' => '评价',
'Sales' => '销量',
'Serve_radius' => '服务半径',
- 'Service_id' => '服务ID',
+ 'Service_id' => '服务项目',
'Service_image' => '服务图片',
'Service_name' => '服务名称',
'Settle_amount' => '结算金额',
@@ -396,7 +396,7 @@ return [
'Custom' => '用户自主定价',
'普通发票' => '普通发票',
'专用发票' => '专用发票',
- 'User_id' => '用户ID',
+ 'User_id' => '关联用户',
'Avatar' => '头像',
'City' => '城市',
'Level' => '等级',
diff --git a/server/application/admin/lang/zh-cn/addon.php b/server/application/admin/lang/zh-cn/addon.php
index abbcb25..32af835 100755
--- a/server/application/admin/lang/zh-cn/addon.php
+++ b/server/application/admin/lang/zh-cn/addon.php
@@ -1,7 +1,7 @@
'ID',
+ 'Id' => '编号',
'Title' => '名称',
'Value' => '配置值',
'Array key' => '键',
diff --git a/server/application/admin/lang/zh-cn/appeal.php b/server/application/admin/lang/zh-cn/appeal.php
index 5361afb..5c34f16 100644
--- a/server/application/admin/lang/zh-cn/appeal.php
+++ b/server/application/admin/lang/zh-cn/appeal.php
@@ -1,12 +1,19 @@
'误判',
- 'Review' => '评价',
- 'Deduct' => '扣款',
- 'Other' => '其他',
- 'Wait' => '待处理',
- 'Handling' => '处理中',
- 'Done' => '已完成',
- 'Reject' => '已驳回',
+ 'Master_id' => '申诉师傅',
+ 'Order_id' => '关联工单',
+ 'Type' => '申诉类型',
+ 'Content' => '申诉详细事由',
+ 'Images' => '证据凭证截图',
+ 'Status' => '工单流转状态',
+ 'Reply' => '平台处理答复',
+ 'Misjudge' => '判责误判',
+ 'Review' => '恶意差评',
+ 'Deduct' => '争议扣款',
+ 'Other' => '其他诉求',
+ 'Wait' => '待平台受理',
+ 'Handling' => '核实处理中',
+ 'Done' => '处理已完结',
+ 'Reject' => '已驳回诉求',
];
diff --git a/server/application/admin/lang/zh-cn/category.php b/server/application/admin/lang/zh-cn/category.php
index 2092274..3e481b5 100755
--- a/server/application/admin/lang/zh-cn/category.php
+++ b/server/application/admin/lang/zh-cn/category.php
@@ -1,18 +1,21 @@
'ID',
- 'Pid' => '父ID',
- 'Type' => '类型',
- 'All' => '全部',
- 'Image' => '图片',
- 'Keywords' => '关键字',
- 'Description' => '描述',
- 'Diyname' => '自定义名称',
+ 'Id' => '编号',
+ 'Pid' => '所属上级类目',
+ 'Type' => '栏目类型',
+ 'All' => '全部类目',
+ 'Name' => '分类名称',
+ 'Nickname' => '分类简称/别名',
+ 'Flag' => '推荐属性标志',
+ 'Image' => '类目图标/图片',
+ 'Keywords' => 'SEO关键字',
+ 'Description' => '类目描述说明',
+ 'Diyname' => '自定义标识URL',
'Createtime' => '创建时间',
'Updatetime' => '更新时间',
- 'Weigh' => '权重',
- 'Category warmtips' => '温馨提示:栏目类型请前往常规管理 ->系统配置 ->字典配置 中进行管理',
- 'Can not change the parent to child or itself' => '父组别不能是它的子组别或它自己',
- 'Status' => '状态'
+ 'Weigh' => '显示排序权重',
+ 'Category warmtips' => '温馨提示:栏目类型请前往常规管理 -> 系统配置 -> 字典配置 中进行管理',
+ 'Can not change the parent to child or itself' => '所属上级类目不能设置为它的子类目或它自身',
+ 'Status' => '上架状态'
];
diff --git a/server/application/admin/lang/zh-cn/dashboard.php b/server/application/admin/lang/zh-cn/dashboard.php
index 66ce16e..99063e2 100755
--- a/server/application/admin/lang/zh-cn/dashboard.php
+++ b/server/application/admin/lang/zh-cn/dashboard.php
@@ -2,7 +2,7 @@
return [
'Custom' => '自定义',
- 'Pid' => '父ID',
+ 'Pid' => '所属上级',
'Type' => '栏目类型',
'Image' => '图片',
'Total user' => '总会员数',
diff --git a/server/application/admin/lang/zh-cn/general/attachment.php b/server/application/admin/lang/zh-cn/general/attachment.php
index 1dc6cae..ad81e7c 100755
--- a/server/application/admin/lang/zh-cn/general/attachment.php
+++ b/server/application/admin/lang/zh-cn/general/attachment.php
@@ -1,9 +1,9 @@
'ID',
- 'Admin_id' => '管理员ID',
- 'User_id' => '会员ID',
+ 'Id' => '附件编号',
+ 'Admin_id' => '上传管理员',
+ 'User_id' => '上传用户',
'Url' => '物理路径',
'Imagewidth' => '宽度',
'Imageheight' => '高度',
diff --git a/server/application/admin/lang/zh-cn/general/dashboard.php b/server/application/admin/lang/zh-cn/general/dashboard.php
index c096c62..5fdccab 100644
--- a/server/application/admin/lang/zh-cn/general/dashboard.php
+++ b/server/application/admin/lang/zh-cn/general/dashboard.php
@@ -2,7 +2,7 @@
return [
'Custom' => '自定义',
- 'Pid' => '父ID',
+ 'Pid' => '所属上级',
'Type' => '栏目类型',
'Image' => '图片',
'Total user' => '总会员数',
diff --git a/server/application/admin/lang/zh-cn/invoice.php b/server/application/admin/lang/zh-cn/invoice.php
index 9f91c1a..8fc630d 100644
--- a/server/application/admin/lang/zh-cn/invoice.php
+++ b/server/application/admin/lang/zh-cn/invoice.php
@@ -1,9 +1,19 @@
'普通发票',
- 'Special' => '专用发票',
- 'Wait' => '开票中',
- 'Issued' => '已开票',
- 'Reject' => '已驳回',
+ 'Merchant_id' => '申请商户',
+ 'User_id' => '申请人/用户',
+ 'Amount' => '开票开具金额(元)',
+ 'Type' => '发票资质类型',
+ 'Title' => '开票抬头名称',
+ 'Tax_no' => '纳税人识别号(税号)',
+ 'Email' => '电子发票接收邮箱',
+ 'Remark' => '开票申请备注',
+ 'Status' => '开票处理状态',
+ 'Invoice_no' => '发票代码与号码',
+ 'Ordinary' => '普通增值税电子发票',
+ 'Special' => '增值税专用发票',
+ 'Wait' => '待财务开具',
+ 'Issued' => '已开票寄送',
+ 'Reject' => '已驳回开票',
];
diff --git a/server/application/admin/lang/zh-cn/master.php b/server/application/admin/lang/zh-cn/master.php
index 0d501f3..4068836 100644
--- a/server/application/admin/lang/zh-cn/master.php
+++ b/server/application/admin/lang/zh-cn/master.php
@@ -1,9 +1,31 @@
'已暂停接单',
- 'On' => '接单中',
- 'Pending' => '待审核',
- 'Pass' => '已通过',
- 'Reject' => '未通过',
+ 'User_id' => '关联用户账号',
+ 'Realname' => '真实姓名',
+ 'Mobile' => '联系电话',
+ 'Idcard_no' => '身份证号',
+ 'Idcard_front' => '身份证人像面',
+ 'Idcard_back' => '身份证国徽面',
+ 'Avatar' => '师傅头像',
+ 'Cert_images' => '资质证书图片',
+ 'City' => '服务常驻城市',
+ 'Lng' => '服务经度',
+ 'Lat' => '服务纬度',
+ 'Serve_radius' => '服务辐射半径(km)',
+ 'Level' => '服务星级/等级',
+ 'Score' => '师傅综合评分',
+ 'Order_count' => '历史完工单数',
+ 'Accept_rate' => '接单率(%)',
+ 'Ontime_rate' => '准时率(%)',
+ 'Work_status' => '接单出工状态',
+ 'Verify_status' => '入驻审核状态',
+ 'Audit_msg' => '审核说明/原因',
+ 'Money' => '可用钱包余额',
+ 'Frozen_money' => '履约冻结金额',
+ 'Off' => '已暂停接单',
+ 'On' => '接单中',
+ 'Pending' => '待审核',
+ 'Pass' => '已通过',
+ 'Reject' => '未通过',
];
diff --git a/server/application/admin/lang/zh-cn/merchant.php b/server/application/admin/lang/zh-cn/merchant.php
index 3d3f0d3..e488e8f 100644
--- a/server/application/admin/lang/zh-cn/merchant.php
+++ b/server/application/admin/lang/zh-cn/merchant.php
@@ -1,9 +1,28 @@
'预付',
- 'Monthly' => '月结',
- 'Pending' => '待审核',
- 'Pass' => '已通过',
- 'Reject' => '未通过',
+ 'User_id' => '关联用户账号',
+ 'Name' => '商户/企业名称',
+ 'License_no' => '统一社会信用代码/营业执照号',
+ 'License_image' => '营业执照原件图',
+ 'Legal_person' => '法定代表人',
+ 'Contact' => '业务对接人',
+ 'Mobile' => '对接人手机号',
+ 'Province' => '所属省份',
+ 'City' => '所在城市',
+ 'District' => '所在区县',
+ 'Address' => '详细办公地址',
+ 'Invoice_title' => '发票抬头',
+ 'Invoice_tax_no' => '纳税人识别号',
+ 'Invoice_bank' => '开户银行',
+ 'Invoice_account' => '开户银行账号',
+ 'Money' => '商户账户余额',
+ 'Frozen_money' => '履约冻结资金',
+ 'Settle_type' => '结算合作模式',
+ 'Status' => '商户运营状态',
+ 'Prepay' => '预付款充值',
+ 'Monthly' => '月结对账',
+ 'Pending' => '待审核',
+ 'Pass' => '审核通过',
+ 'Reject' => '审核驳回',
];
diff --git a/server/application/admin/lang/zh-cn/message.php b/server/application/admin/lang/zh-cn/message.php
index ab6cb8a..5a0acdd 100644
--- a/server/application/admin/lang/zh-cn/message.php
+++ b/server/application/admin/lang/zh-cn/message.php
@@ -1,10 +1,19 @@
'用户',
- 'Master' => '师傅',
- 'System' => '系统',
- 'Audit' => '审核',
- 'Remind' => '提醒',
- 'Order' => '订单',
+ 'Receive_type' => '接收人群体',
+ 'Receive_id' => '接收目标对象',
+ 'Type' => '消息业务类型',
+ 'Title' => '消息通知标题',
+ 'Content' => '消息正文详情',
+ 'Order_id' => '关联业务工单',
+ 'Is_read' => '消息已读标记',
+ 'Read_yes' => '已读',
+ 'Read_no' => '未读',
+ 'User' => '普通雇主用户',
+ 'Master' => '入驻签约师傅',
+ 'System' => '系统通知',
+ 'Audit' => '资格审核提醒',
+ 'Remind' => '履约工单提醒',
+ 'Order' => '订单状态流转',
];
diff --git a/server/application/admin/lang/zh-cn/order.php b/server/application/admin/lang/zh-cn/order.php
index 5fc8764..27d6ac3 100644
--- a/server/application/admin/lang/zh-cn/order.php
+++ b/server/application/admin/lang/zh-cn/order.php
@@ -1,55 +1,55 @@
'订单号',
- 'User_id' => '下单用户',
- 'Master_id' => '接单师傅',
- 'Service_id' => '服务ID',
- 'Cat_id' => '类目ID',
- 'Service_name' => '服务名称',
+ 'Order_no' => '订单编号',
+ 'User_id' => '下单客户',
+ 'Master_id' => '指派/接单师傅',
+ 'Service_id' => '预约服务项目',
+ 'Cat_id' => '所属服务类目',
+ 'Service_name' => '服务项目名称',
'Service_image' => '服务主图',
- 'Goods_images' => '商品图片',
- 'Goods_model' => '商品型号',
- 'Goods_num' => '商品数量',
- 'Floor' => '楼层',
+ 'Goods_images' => '现场/商品图片',
+ 'Goods_model' => '规格/商品型号',
+ 'Goods_num' => '服务数量',
+ 'Floor' => '目标楼层',
'Has_elevator' => '有无电梯',
- 'Urgent' => '是否加急',
- 'Remark' => '备注/施工要求',
- 'Contact' => '联系人',
- 'Mobile' => '联系电话',
+ 'Urgent' => '加急服务',
+ 'Remark' => '客户备注/施工要求',
+ 'Contact' => '服务联系人',
+ 'Mobile' => '联系人电话',
'Province' => '省份',
- 'City' => '城市',
- 'District' => '区县',
- 'Address' => '详细地址',
- 'Lng' => '经度',
- 'Lat' => '纬度',
+ 'City' => '服务城市',
+ 'District' => '服务区县',
+ 'Address' => '上门详细地址',
+ 'Lng' => '经度坐标',
+ 'Lat' => '纬度坐标',
'Appoint_time' => '预约上门时间',
- 'Base_amount' => '基础价',
- 'Floor_amount' => '楼层费',
- 'Distance_amount' => '距离费',
- 'Urgent_amount' => '加急费',
- 'Discount_amount' => '优惠金额',
- 'Total_amount' => '订单总额',
- 'Pay_amount' => '实付金额',
- 'Status' => '订单状态',
+ 'Base_amount' => '服务基础费',
+ 'Floor_amount' => '步梯楼层费',
+ 'Distance_amount' => '远程距离费',
+ 'Urgent_amount' => '加急服务费',
+ 'Discount_amount' => '优惠减免金额',
+ 'Total_amount' => '订单总应付金额',
+ 'Pay_amount' => '客户实付金额',
+ 'Status' => '订单履约状态',
'Pay_status' => '支付状态',
- 'Pay_type' => '支付方式',
- 'Pay_time' => '支付时间',
- 'Transaction_id' => '交易流水号',
- 'Coupon_id' => '优惠券ID',
- 'Accept_time' => '接单时间',
- 'Contact_time' => '联系客户时间',
- 'Arrive_time' => '到达时间',
- 'Start_time' => '开始服务时间',
- 'Finish_time' => '完工时间',
- 'Verify_time' => '验收时间',
- 'Verify_code' => '验收码',
- 'Settle_status' => '结算状态',
- 'Settle_amount' => '结算金额',
- 'Cancel_reason' => '取消原因',
+ 'Pay_type' => '支付渠道方式',
+ 'Pay_time' => '支付到账时间',
+ 'Transaction_id' => '第三方交易单号',
+ 'Coupon_id' => '使用优惠券',
+ 'Accept_time' => '师傅接单时间',
+ 'Contact_time' => '预约联系时间',
+ 'Arrive_time' => '上门到达时间',
+ 'Start_time' => '开工施工时间',
+ 'Finish_time' => '完工提交时间',
+ 'Verify_time' => '客户验收时间',
+ 'Verify_code' => '完工验收核销码',
+ 'Settle_status' => '师傅结算状态',
+ 'Settle_amount' => '师傅结算佣金金额',
+ 'Cancel_reason' => '订单取消原因',
'Refund_amount' => '退款金额',
- 'Is_review' => '是否已评价',
- 'Price_type' => '计价方式',
- 'Createtime' => '创建时间',
- 'Updatetime' => '更新时间',
+ 'Is_review' => '服务评价状态',
+ 'Price_type' => '计价结算模式',
+ 'Createtime' => '下单创建时间',
+ 'Updatetime' => '状态更新时间',
];
diff --git a/server/application/admin/lang/zh-cn/review.php b/server/application/admin/lang/zh-cn/review.php
index 625fa5a..0d4e973 100644
--- a/server/application/admin/lang/zh-cn/review.php
+++ b/server/application/admin/lang/zh-cn/review.php
@@ -1,7 +1,16 @@
'星级(1-5)',
- 'Tags' => '标签',
- 'Reply' => '师傅回复',
+ 'Order_id' => '关联工单',
+ 'User_id' => '评价雇主',
+ 'Master_id' => '履约师傅',
+ 'Score' => '服务评分(星级)',
+ 'Tags' => '评价印象标签',
+ 'Content' => '雇主评价留言',
+ 'Images' => '现场实拍晒图',
+ 'Is_anonymous' => '是否匿名评价',
+ 'Reply' => '师傅服务回复',
+ 'Status' => '评价审核状态',
+ 'Anonymous_yes'=> '匿名',
+ 'Anonymous_no' => '公开',
];
diff --git a/server/application/admin/lang/zh-cn/service.php b/server/application/admin/lang/zh-cn/service.php
index 1bfce17..b08adc6 100644
--- a/server/application/admin/lang/zh-cn/service.php
+++ b/server/application/admin/lang/zh-cn/service.php
@@ -1,16 +1,16 @@
'类目ID',
- 'Name' => '服务名称',
- 'Image' => '主图',
+ 'Cat_id' => '所属服务类目',
+ 'Name' => '服务项目名称',
+ 'Image' => '服务展示主图',
'Price_type' => '计价方式',
- 'Base_price' => '基础价',
- 'Max_price' => '区间上限',
+ 'Base_price' => '基础基准价(元)',
+ 'Max_price' => '区间上限价(元)',
'Unit' => '计价单位',
'Floor_fee' => '楼层费(元/层)',
'Free_floor' => '免费楼层数',
'Need_model' => '是否需填型号',
- 'Description' => '服务说明',
- 'Sales' => '销量',
+ 'Description' => '服务详细说明',
+ 'Sales' => '累计销量',
];
diff --git a/server/application/admin/lang/zh-cn/service/cat.php b/server/application/admin/lang/zh-cn/service/cat.php
index 9e4609d..b3f5513 100644
--- a/server/application/admin/lang/zh-cn/service/cat.php
+++ b/server/application/admin/lang/zh-cn/service/cat.php
@@ -1,8 +1,8 @@
'父级ID',
+ 'Pid' => '所属上级类目',
'Name' => '类目名称',
- 'Icon' => '图标标识',
- 'Weigh' => '权重',
+ 'Icon' => '类目图标',
+ 'Weigh' => '排序权重',
];
diff --git a/server/application/admin/lang/zh-cn/settle.php b/server/application/admin/lang/zh-cn/settle.php
index 203926e..520c12a 100644
--- a/server/application/admin/lang/zh-cn/settle.php
+++ b/server/application/admin/lang/zh-cn/settle.php
@@ -1,7 +1,15 @@
'待结算',
- 'Settled' => '已结算',
- 'Offline' => '线下结算',
+ 'Order_id' => '关联工单',
+ 'Master_id' => '结算师傅',
+ 'Order_amount' => '工单原始金额(元)',
+ 'Commission_rate' => '平台抽成比例(%)',
+ 'Commission' => '平台抽佣金额(元)',
+ 'Settle_amount' => '师傅应结所得(元)',
+ 'Status' => '结算流转状态',
+ 'Settle_time' => '实际打款时间',
+ 'Wait' => '待财务结算',
+ 'Settled' => '已划扣结算',
+ 'Offline' => '线下特殊结算',
];
diff --git a/server/application/admin/lang/zh-cn/user/rule.php b/server/application/admin/lang/zh-cn/user/rule.php
index 35e3a2e..b55fc04 100755
--- a/server/application/admin/lang/zh-cn/user/rule.php
+++ b/server/application/admin/lang/zh-cn/user/rule.php
@@ -1,7 +1,7 @@
'父ID',
+ 'Pid' => '所属上级',
'Name' => '规则',
'Title' => '标题',
'Remark' => '备注',
diff --git a/server/application/admin/lang/zh-cn/user/user.php b/server/application/admin/lang/zh-cn/user/user.php
index 13f096d..aa451bd 100755
--- a/server/application/admin/lang/zh-cn/user/user.php
+++ b/server/application/admin/lang/zh-cn/user/user.php
@@ -1,8 +1,8 @@
'ID',
- 'Group_id' => '组别ID',
+ 'Id' => '会员编号',
+ 'Group_id' => '所属会员分组',
'Username' => '用户名',
'Nickname' => '昵称',
'Password' => '密码',
diff --git a/server/application/admin/lang/zh-cn/withdraw.php b/server/application/admin/lang/zh-cn/withdraw.php
index b7efc5e..ff64fa1 100644
--- a/server/application/admin/lang/zh-cn/withdraw.php
+++ b/server/application/admin/lang/zh-cn/withdraw.php
@@ -1,10 +1,18 @@
'微信',
- 'Bank' => '银行卡',
- 'Wait' => '待审核',
- 'Pass' => '已通过',
- 'Reject' => '已驳回',
- 'Done' => '已打款',
+ 'Master_id' => '提现申请师傅',
+ 'Amount' => '申请提现总额(元)',
+ 'Fee' => '提现手续费(元)',
+ 'Arrive_amount' => '预计到账金额(元)',
+ 'Channel' => '提现结算渠道',
+ 'Account' => '打款结算账号/卡号',
+ 'Status' => '提现审核状态',
+ 'Audit_msg' => '审核备注/驳回说明',
+ 'Wechat' => '微信钱包',
+ 'Bank' => '银行储蓄卡',
+ 'Wait' => '待财务审核',
+ 'Pass' => '审核通过',
+ 'Reject' => '已驳回申请',
+ 'Done' => '已成功打款',
];
diff --git a/server/application/admin/view/appeal/add.html b/server/application/admin/view/appeal/add.html
index aebac76..9943c89 100644
--- a/server/application/admin/view/appeal/add.html
+++ b/server/application/admin/view/appeal/add.html
@@ -1,71 +1,108 @@