change(internal): 按场景对微信插件进行分类,优化相关代码 (#909)

This commit is contained in:
yansongda 2023-12-31 18:00:00 +08:00 committed by GitHub
parent d1dcd340bc
commit a849c761c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
493 changed files with 16958 additions and 9493 deletions

View File

@ -19,5 +19,6 @@ return (new PhpCsFixer\Config())
'import_functions' => true,
],
'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'],
'fully_qualified_strict_types' => true,
])
->setFinder($finder);

View File

@ -3,19 +3,19 @@
### added
- feat: 新增 `InvalidSignException`(#903)
- feat: 新增 `DecryptException`(#906)
### changed
- change(internal): shortcut 完整标明各个插件,不使用 commonPlugin(#886)
- change(internal): 按场景对支付宝插件进行分类(#894)
- change(internal): 支付宝 shortcut 从 plugin 文件夹独立出来(#895)
- change(internal): DirectionInterface 方法由 `parse` 改为 `guide`(#896)
- change: 查询API方法由 `find` 改为 `query`,同时参数只支持 array(#897)
- change: cancel 的 API 参数只支持 array不再支持 string(#900)
- change: close 的 API 参数只支持 array不再支持 string(#901)
- change(internal): 错误代码 const 命名规则统一(#902, #903)
- change(internal): 微信 shortcut 从 plugin 文件夹独立出来(#904)
- change(internal): 银联 shortcut 从 plugin 文件夹独立出来(#905)
- change: cancel/close 的 API 参数只支持 array不再支持 string(#900, #901)
- change: 微信合单支付去掉独立的 `combine_app_id`,`combine_mch_id` 配置,复用其它配置(#909)
- change(internal): 按场景对 支付宝/微信/银联 插件进行分类 && 插件代码优化(#894, #909)
- change(internal): 将 支付/微信/银联 shortcut 从 plugin 文件夹独立出来(#895, #904, #905)
- change(internal): shortcut 完整标明各个插件,不使用 commonPlugin(#886)
- change(internal): DirectionInterface 方法由 `parse` 改为 `guide`(#896)
- change(internal): 错误代码 const 命名规则统一(#902, #903, #906, #909)
- change(internal): 调整 `ProviderInterface` 的返回参数,增加了 `Rocket` 返回(#909)
## v3.5.3

View File

@ -27,12 +27,12 @@
"psr/container": "^1.1 || ^2.0",
"psr/http-client": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"yansongda/supports": "~4.0.0"
"yansongda/supports": "~4.0.4"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"mockery/mockery": "^1.4",
"friendsofphp/php-cs-fixer": "^3.0",
"friendsofphp/php-cs-fixer": "^3.44",
"phpstan/phpstan": "^1.0.0",
"monolog/monolog": "^2.2",
"symfony/var-dumper": "^5.1",

View File

@ -10,6 +10,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
interface ProviderInterface
@ -19,17 +20,17 @@ interface ProviderInterface
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function pay(array $plugins, array $params): null|array|Collection|MessageInterface;
public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket;
public function query(array $order): array|Collection;
public function query(array $order): Collection|Rocket;
public function cancel(array $order): null|array|Collection;
public function cancel(array $order): Collection|Rocket;
public function close(array $order): null|array|Collection;
public function close(array $order): Collection|Rocket;
public function refund(array $order): array|Collection;
public function refund(array $order): Collection|Rocket;
public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection;
public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket;
public function success(): ResponseInterface;
}

View File

@ -19,7 +19,7 @@ class CollectionDirection implements DirectionInterface
public function guide(PackerInterface $packer, ?ResponseInterface $response): Collection
{
if (is_null($response)) {
throw new InvalidResponseException(Exception::RESPONSE_EMPTY);
throw new InvalidResponseException(Exception::RESPONSE_EMPTY, '响应异常: 响应为空,不能进行 direction');
}
$body = (string) $response->getBody();
@ -28,6 +28,6 @@ class CollectionDirection implements DirectionInterface
return new Collection($result);
}
throw new InvalidResponseException(Exception::RESPONSE_UNPACK_ERROR, 'Unpack Response Error', ['body' => $body, 'response' => $response]);
throw new InvalidResponseException(Exception::RESPONSE_UNPACK_ERROR, '响应异常: 解包错误', ['body' => $body, 'response' => $response]);
}
}

View File

@ -17,10 +17,10 @@ class OriginResponseDirection implements DirectionInterface
*/
public function guide(PackerInterface $packer, ?ResponseInterface $response): ?ResponseInterface
{
if (!is_null($response)) {
return $response;
if (is_null($response)) {
throw new InvalidResponseException(Exception::RESPONSE_EMPTY, '响应异常: 响应为空,不能进行 direction');
}
throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG);
return $response;
}
}

View File

@ -33,6 +33,6 @@ class Event
return;
}
throw new InvalidConfigException(Exception\Exception::CONFIG_EVENT_INVALID);
throw new InvalidConfigException(Exception\Exception::CONFIG_EVENT_INVALID, '配置异常: 配置的 `EventDispatcherInterface` 不符合 PSR 规范');
}
}

View File

@ -9,7 +9,7 @@ use Throwable;
class ContainerException extends Exception implements ContainerExceptionInterface
{
public function __construct(string $message = '', int $code = self::CONTAINER_ERROR, mixed $extra = null, ?Throwable $previous = null)
public function __construct(string $message = '容器异常', int $code = self::CONTAINER_ERROR, mixed $extra = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $extra, $previous);
}

View File

@ -8,7 +8,7 @@ use Throwable;
class ContainerNotFoundException extends ContainerException
{
public function __construct(string $message = 'Container Not Found', int $code = self::CONTAINER_NOT_FOUND, mixed $extra = null, ?Throwable $previous = null)
public function __construct(string $message = '容器未找到', int $code = self::CONTAINER_NOT_FOUND, mixed $extra = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $extra, $previous);
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Exception;
use Throwable;
class DecryptException extends Exception
{
public function __construct(
int $code = self::DECRYPT_ERROR,
string $message = '加解密异常',
mixed $extra = null,
?Throwable $previous = null
) {
parent::__construct($message, $code, $extra, $previous);
}
}

View File

@ -60,13 +60,21 @@ class Exception extends \Exception
public const PARAMS_METHOD_NOT_SUPPORTED = 4004;
public const PARAMS_REQUEST_EMPTY = 4005;
public const PARAMS_NECESSARY_PARAMS_MISSING = 4005;
public const PARAMS_NECESSARY_PARAMS_MISSING = 4006;
public const PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE = 4006;
public const PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE = 4007;
public const PARAMS_WECHAT_SERIAL_NOT_FOUND = 4007;
public const PARAMS_WECHAT_SERIAL_NOT_FOUND = 4008;
public const PARAMS_CALLBACK_REQUEST_INVALID = 4008;
public const PARAMS_WECHAT_URL_MISSING = 4009;
public const PARAMS_WECHAT_BODY_MISSING = 4010;
public const PARAMS_WECHAT_AUTHORIZATION_MISSING = 4011;
public const PARAMS_PLUGIN_ONLY_SUPPORT_NORMAL_MODE = 4012;
/**
* 关于响应.
@ -77,17 +85,11 @@ class Exception extends \Exception
public const RESPONSE_UNPACK_ERROR = 5002;
public const RESPONSE_CODE_WRONG = 5004;
public const RESPONSE_CODE_WRONG = 5003;
public const RESPONSE_MISSING_NECESSARY_PARAMS = 5005;
public const RESPONSE_MISSING_NECESSARY_PARAMS = 5004;
public const RESPONSE_EMPTY = 5006;
public const RESPONSE_CIPHERTEXT_PARAMS_INVALID = 5007;
public const RESPONSE_ENCRYPTED_DATA_INVALID = 5008;
public const RESPONSE_DECRYPTED_METHOD_INVALID = 5009;
public const RESPONSE_EMPTY = 5005;
/**
* 关于签名.
@ -96,9 +98,20 @@ class Exception extends \Exception
public const SIGN_EMPTY = 6001;
/**
* 关于加解密.
*/
public const DECRYPT_ERROR = 7000;
public const DECRYPT_WECHAT_CIPHERTEXT_PARAMS_INVALID = 7001;
public const DECRYPT_WECHAT_ENCRYPTED_DATA_INVALID = 7002;
public const DECRYPT_WECHAT_DECRYPTED_METHOD_INVALID = 7003;
public mixed $extra;
public function __construct(string $message = 'Unknown Error', int $code = self::UNKNOWN_ERROR, mixed $extra = null, ?Throwable $previous = null)
public function __construct(string $message = '未知异常', int $code = self::UNKNOWN_ERROR, mixed $extra = null, ?Throwable $previous = null)
{
$this->extra = $extra;

View File

@ -8,7 +8,7 @@ use Throwable;
class InvalidConfigException extends Exception
{
public function __construct(int $code = self::CONFIG_ERROR, string $message = 'Config Error', mixed $extra = null, ?Throwable $previous = null)
public function __construct(int $code = self::CONFIG_ERROR, string $message = '配置异常', mixed $extra = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $extra, $previous);
}

View File

@ -8,7 +8,7 @@ use Throwable;
class InvalidParamsException extends Exception
{
public function __construct(int $code = self::PARAMS_ERROR, string $message = 'Params Error', mixed $extra = null, ?Throwable $previous = null)
public function __construct(int $code = self::PARAMS_ERROR, string $message = '参数异常', mixed $extra = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $extra, $previous);
}

View File

@ -12,7 +12,7 @@ class InvalidResponseException extends Exception
public function __construct(
int $code = self::RESPONSE_ERROR,
string $message = 'Provider response Error',
string $message = '响应异常',
mixed $extra = null,
?Throwable $previous = null
) {

View File

@ -12,7 +12,7 @@ class InvalidSignException extends Exception
public function __construct(
int $code = self::SIGN_ERROR,
string $message = 'Sign Error',
string $message = '签名异常',
mixed $extra = null,
?Throwable $previous = null
) {

View File

@ -9,7 +9,7 @@ use Throwable;
class ServiceNotFoundException extends Exception implements NotFoundExceptionInterface
{
public function __construct(string $message = 'Service Not Found', int $code = self::SERVICE_NOT_FOUND, mixed $extra = null, ?Throwable $previous = null)
public function __construct(string $message = '服务未找到', int $code = self::SERVICE_NOT_FOUND, mixed $extra = null, ?Throwable $previous = null)
{
parent::__construct($message, $code, $extra, $previous);
}

View File

@ -4,23 +4,28 @@ declare(strict_types=1);
namespace Yansongda\Pay;
use Closure;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yansongda\Pay\Contract\ConfigInterface;
use Yansongda\Pay\Contract\DirectionInterface;
use Yansongda\Pay\Direction\NoHttpRequestDirection;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\DecryptException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\InvalidResponseException;
use Yansongda\Pay\Exception\InvalidSignException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\ParserPlugin;
use Yansongda\Pay\Plugin\Wechat\PreparePlugin;
use Yansongda\Pay\Plugin\Wechat\RadarSignPlugin;
use Yansongda\Pay\Plugin\Wechat\AddPayloadBodyPlugin;
use Yansongda\Pay\Plugin\Wechat\AddPayloadSignaturePlugin;
use Yansongda\Pay\Plugin\Wechat\AddRadarPlugin;
use Yansongda\Pay\Plugin\Wechat\ResponsePlugin;
use Yansongda\Pay\Plugin\Wechat\StartPlugin;
use Yansongda\Pay\Plugin\Wechat\WechatPublicCertsPlugin;
use Yansongda\Pay\Provider\Wechat;
use Yansongda\Supports\Collection;
use Yansongda\Supports\Str;
function should_do_http_request(string $direction): bool
@ -47,23 +52,12 @@ function get_direction(mixed $direction): DirectionInterface
}
if (!$direction instanceof DirectionInterface) {
throw new InvalidConfigException(Exception::CONFIG_DIRECTION_INVALID);
throw new InvalidConfigException(Exception::CONFIG_DIRECTION_INVALID, '配置异常: 配置的 DirectionInterface 未实现 `DirectionInterface`');
}
return $direction;
}
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
function get_alipay_config(array $params = []): array
{
$alipay = Pay::get(ConfigInterface::class)->get('alipay');
return $alipay[get_tenant($params)] ?? [];
}
function get_public_cert(string $key): string
{
return Str::endsWith($key, ['.cer', '.crt', '.pem']) ? file_get_contents($key) : $key;
@ -80,18 +74,32 @@ function get_private_cert(string $key): string
"\n-----END RSA PRIVATE KEY-----";
}
function filter_params(array $params, ?Closure $closure = null): array
{
return array_filter($params, static fn ($v, $k) => !Str::startsWith($k, '_') && !is_null($v) && (empty($closure) || $closure($k, $v)), ARRAY_FILTER_USE_BOTH);
}
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws ServiceNotFoundException
*/
function get_alipay_config(array $params = []): array
{
$alipay = Pay::get(ConfigInterface::class)->get('alipay');
return $alipay[get_tenant($params)] ?? [];
}
/**
* @throws InvalidConfigException
* @throws InvalidSignException
*/
function verify_alipay_sign(array $params, string $contents, string $sign): void
function verify_alipay_sign(array $config, string $contents, string $sign): void
{
$public = get_alipay_config($params)['alipay_public_cert_path'] ?? null;
$public = $config['alipay_public_cert_path'] ?? null;
if (empty($public)) {
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, 'Missing Alipay Config -- [alipay_public_cert_path]');
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [alipay_public_cert_path]');
}
$result = 1 === openssl_verify(
@ -102,7 +110,7 @@ function verify_alipay_sign(array $params, string $contents, string $sign): void
);
if (!$result) {
throw new InvalidSignException(Exception::SIGN_ERROR, 'Verify Alipay Sign Failed', func_get_args());
throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证支付宝签名失败', func_get_args());
}
}
@ -110,35 +118,74 @@ function verify_alipay_sign(array $params, string $contents, string $sign): void
* @throws ContainerException
* @throws ServiceNotFoundException
*/
function get_wechat_config(array $params): array
function get_wechat_config(array $params = []): array
{
$wechat = Pay::get(ConfigInterface::class)->get('wechat');
return $wechat[get_tenant($params)] ?? [];
}
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
function get_wechat_base_uri(array $params): string
function get_wechat_method(?Collection $payload): string
{
$config = get_wechat_config($params);
return Wechat::URL[$config['mode'] ?? Pay::MODE_NORMAL];
return strtoupper($payload?->get('_method') ?? 'POST');
}
/**
* @throws InvalidParamsException
*/
function get_wechat_url(array $config, ?Collection $payload): string
{
$url = $payload?->get('_url') ?? null;
if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
$url = $payload?->get('_service_url') ?? $url ?? null;
}
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): string
{
$body = $payload?->get('_body') ?? null;
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 ContainerException
* @throws ServiceNotFoundException
* @throws InvalidConfigException
*/
function get_wechat_sign(array $params, string $contents): string
function get_wechat_sign(array $config, string $contents): string
{
$privateKey = get_wechat_config($params)['mch_secret_cert'] ?? null;
$privateKey = $config['mch_secret_cert'] ?? null;
if (empty($privateKey)) {
throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, 'Missing Wechat Config -- [mch_secret_cert]');
throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_cert]');
}
$privateKey = get_private_cert($privateKey);
@ -158,7 +205,7 @@ function get_wechat_sign_v2(array $params, array $payload, bool $upper = true):
$key = get_wechat_config($params)['mch_secret_key_v2'] ?? null;
if (empty($key)) {
throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, 'Missing Wechat Config -- [mch_secret_key_v2]');
throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 缺少微信配置 -- [mch_secret_key_v2]');
}
ksort($payload);
@ -176,11 +223,11 @@ function get_wechat_sign_v2(array $params, array $payload, bool $upper = true):
/**
* @throws ContainerException
* @throws DecryptException
* @throws InvalidConfigException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
* @throws InvalidParamsException
* @throws InvalidSignException
* @throws ServiceNotFoundException
*/
function verify_wechat_sign(ResponseInterface|ServerRequestInterface $message, array $params): void
{
@ -198,7 +245,7 @@ function verify_wechat_sign(ResponseInterface|ServerRequestInterface $message, a
$public = get_wechat_config($params)['wechat_public_cert_path'][$wechatSerial] ?? null;
if (empty($sign)) {
throw new InvalidSignException(Exception::SIGN_EMPTY, $body, ['headers' => $message->getHeaders(), 'body' => $body]);
throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 微信签名为空', ['headers' => $message->getHeaders(), 'body' => $body]);
}
$public = get_public_cert(
@ -213,7 +260,7 @@ function verify_wechat_sign(ResponseInterface|ServerRequestInterface $message, a
);
if (!$result) {
throw new InvalidSignException(Exception::SIGN_ERROR, 'Verify Wechat Sign Failed', ['headers' => $message->getHeaders(), 'body' => $body]);
throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证微信签名失败', ['headers' => $message->getHeaders(), 'body' => $body]);
}
}
@ -228,31 +275,31 @@ function encrypt_wechat_contents(string $contents, string $publicKey): ?string
/**
* @throws ContainerException
* @throws DecryptException
* @throws InvalidConfigException
* @throws ServiceNotFoundException
* @throws InvalidParamsException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
*/
function reload_wechat_public_certs(array $params, ?string $serialNo = null): string
{
$data = Pay::wechat()->pay(
[PreparePlugin::class, WechatPublicCertsPlugin::class, RadarSignPlugin::class, ParserPlugin::class],
[StartPlugin::class, WechatPublicCertsPlugin::class, AddPayloadBodyPlugin::class, AddPayloadSignaturePlugin::class, AddRadarPlugin::class, ResponsePlugin::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);
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, 'Get Wechat Public Cert Error');
throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, '配置异常: 获取微信 wechat_public_cert_path 配置失败');
}
return $certs[$serialNo] ?? '';
@ -260,10 +307,10 @@ function reload_wechat_public_certs(array $params, ?string $serialNo = null): st
/**
* @throws ContainerException
* @throws DecryptException
* @throws InvalidConfigException
* @throws ServiceNotFoundException
* @throws InvalidParamsException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
*/
function get_wechat_public_certs(array $params = [], ?string $path = null): void
{
@ -283,34 +330,32 @@ function get_wechat_public_certs(array $params = [], ?string $path = null): void
}
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
* @throws DecryptException
*/
function decrypt_wechat_resource(array $resource, array $params): array
function decrypt_wechat_resource(array $resource, array $config): array
{
$ciphertext = base64_decode($resource['ciphertext'] ?? '');
$secret = get_wechat_config($params)['mch_secret_key'] ?? null;
$secret = $config['mch_secret_key'] ?? null;
if (strlen($ciphertext) <= Wechat::AUTH_TAG_LENGTH_BYTE) {
throw new InvalidResponseException(Exception::RESPONSE_CIPHERTEXT_PARAMS_INVALID);
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, 'Missing Wechat Config -- [mch_secret_key]');
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 InvalidResponseException(Exception::RESPONSE_DECRYPTED_METHOD_INVALID),
default => throw new DecryptException(Exception::DECRYPT_WECHAT_DECRYPTED_METHOD_INVALID, '加解密异常: algorithm 不支持'),
};
return $resource;
}
/**
* @throws InvalidResponseException
* @throws DecryptException
*/
function decrypt_wechat_resource_aes_256_gcm(string $ciphertext, string $secret, string $nonce, string $associatedData): array|string
{
@ -324,17 +369,61 @@ function decrypt_wechat_resource_aes_256_gcm(string $ciphertext, string $secret,
$associatedData
);
if (false === $decrypted) {
throw new DecryptException(Exception::DECRYPT_WECHAT_ENCRYPTED_DATA_INVALID, '加解密异常: 解密失败,请检查微信 mch_secret_key 是否正确');
}
if ('certificate' !== $associatedData) {
$decrypted = json_decode(strval($decrypted), true);
$decrypted = json_decode($decrypted, true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new InvalidResponseException(Exception::RESPONSE_ENCRYPTED_DATA_INVALID);
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_wechat_config($params);
if (empty($config['wechat_public_cert_path'])) {
reload_wechat_public_certs($params);
$config = get_wechat_config($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 ContainerException
* @throws ServiceNotFoundException
@ -356,7 +445,7 @@ 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::CONFIG_UNIPAY_INVALID, 'Missing Unipay Config -- [unipay_public_cert_path]');
throw new InvalidConfigException(Exception::CONFIG_UNIPAY_INVALID, '配置异常: 缺少银联配置 -- [unipay_public_cert_path]');
}
$result = 1 === openssl_verify(
@ -367,6 +456,6 @@ function verify_unipay_sign(array $params, string $contents, string $sign): void
);
if (!$result) {
throw new InvalidSignException(Exception::SIGN_ERROR, 'Verify Unipay Sign Failed', func_get_args());
throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证银联签名失败', func_get_args());
}
}

View File

@ -43,6 +43,6 @@ class Logger
return;
}
throw new InvalidConfigException(Exception\Exception::CONFIG_LOGGER_INVALID);
throw new InvalidConfigException(Exception\Exception::CONFIG_LOGGER_INVALID, '配置异常: 配置的 `LoggerInterface` 不符合 PSR 规范');
}
}

View File

@ -137,10 +137,10 @@ class Pay
} catch (ContainerNotFoundException $e) {
throw $e;
} catch (Throwable $e) {
throw new ContainerException($e->getMessage());
throw new ContainerException('容器异常: '.$e->getMessage());
}
throw new ContainerException('Current container does NOT support `set` method');
throw new ContainerException('容器异常: 当前容器类型不支持 `set` 方法');
}
/**
@ -159,7 +159,7 @@ class Pay
} catch (ContainerNotFoundException $e) {
throw $e;
} catch (Throwable $e) {
throw new ContainerException($e->getMessage());
throw new ContainerException('容器异常: '.$e->getMessage());
}
$parameters = array_values($parameters);
@ -176,11 +176,11 @@ class Pay
try {
return Pay::getContainer()->get($service);
} catch (NotFoundExceptionInterface $e) {
throw new ServiceNotFoundException($e->getMessage());
throw new ServiceNotFoundException('服务未找到: '.$e->getMessage());
} catch (ContainerNotFoundException $e) {
throw $e;
} catch (Throwable $e) {
throw new ContainerException($e->getMessage());
throw new ContainerException('容器异常: '.$e->getMessage());
}
}
@ -210,7 +210,7 @@ class Pay
return (self::$container)();
}
throw new ContainerNotFoundException('`getContainer()` failed! Maybe you should `setContainer()` first', Exception\Exception::CONTAINER_NOT_FOUND);
throw new ContainerNotFoundException('容器未找到: `getContainer()` failed! Maybe you should `setContainer()` first', Exception\Exception::CONTAINER_NOT_FOUND);
}
public static function hasContainer(): bool

View File

@ -16,7 +16,7 @@ use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_alipay_config;
use function Yansongda\Pay\get_private_cert;
class AddSignaturePlugin implements PluginInterface
class AddPayloadSignaturePlugin implements PluginInterface
{
/**
* @throws ContainerException
@ -25,11 +25,11 @@ class AddSignaturePlugin implements PluginInterface
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Alipay][AddSignaturePlugin] 插件开始装载', ['rocket' => $rocket]);
Logger::debug('[Alipay][AddPayloadSignaturePlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload(['sign' => $this->getSign($rocket)]);
Logger::info('[Alipay][AddSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
Logger::info('[Alipay][AddPayloadSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
@ -60,7 +60,7 @@ class AddSignaturePlugin implements PluginInterface
$privateKey = get_alipay_config($params)['app_secret_cert'] ?? null;
if (is_null($privateKey)) {
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, 'Missing Alipay Config -- [app_secret_cert]');
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [app_secret_cert]');
}
return get_private_cert($privateKey);

View File

@ -15,8 +15,9 @@ use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use Yansongda\Supports\Str;
use function Yansongda\Pay\filter_params;
use function Yansongda\Pay\get_alipay_config;
use function Yansongda\Pay\verify_alipay_sign;
class CallbackPlugin implements PluginInterface
@ -35,10 +36,12 @@ class CallbackPlugin implements PluginInterface
$sign = $rocket->getParams()['sign'] ?? false;
if (!$sign) {
throw new InvalidSignException(Exception::SIGN_EMPTY, 'Callback Empty Sign', $rocket->getParams());
throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 支付宝回调签名为空', $rocket->getParams());
}
verify_alipay_sign($rocket->getParams(), $this->getSignContent($rocket->getPayload()), $sign);
$config = get_alipay_config($rocket->getParams());
verify_alipay_sign($config, $this->getSignContent($rocket->getPayload()), $sign);
$rocket->setDirection(NoHttpRequestDirection::class)
->setDestination($rocket->getPayload());
@ -50,10 +53,9 @@ class CallbackPlugin implements PluginInterface
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, '_'));
$payload = filter_params($rocket->getParams(), fn ($k, $v) => '' !== $v && 'sign' != $k && 'sign_type' != $k);
$rocket->setPayload($payload);
$rocket->setPayload(new Collection($payload));
}
protected function getSignContent(Collection $payload): string

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Str;
class FormatBizContentPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Alipay][FormatBizContentPlugin] 插件开始装载', ['rocket' => $rocket]);
$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)])
);
Logger::info('[Alipay][FormatBizContentPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\filter_params;
class FormatPayloadBizContentPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Alipay][FormatPayloadBizContentPlugin] 插件开始装载', ['rocket' => $rocket]);
$payload = $rocket->getPayload();
$rocket->setPayload(new Collection(array_merge(
filter_params($payload->all(), fn ($k, $v) => '' !== $v && !is_null($v) && 'sign' != $k),
['biz_content' => json_encode(filter_params($payload->get('biz_content', [])))]
)));
Logger::info('[Alipay][FormatPayloadBizContentPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -17,7 +17,7 @@ class SignPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Alipay][Pay][Agreement][AddSignaturePlugin] 插件开始装载', ['rocket' => $rocket]);
Logger::debug('[Alipay][Pay][Agreement][SignPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->setDirection(ResponseDirection::class)
->mergePayload([
@ -25,7 +25,7 @@ class SignPlugin implements PluginInterface
'biz_content' => $rocket->getParams(),
]);
Logger::info('[Alipay][Pay][Agreement][AddSignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);
Logger::info('[Alipay][Pay][Agreement][SignPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Pay\Wap;
namespace Yansongda\Pay\Plugin\Alipay\Pay\H5;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;

View File

@ -106,7 +106,7 @@ class StartPlugin implements PluginInterface
$path = $config['app_public_cert_path'] ?? null;
if (is_null($path)) {
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, 'Missing Alipay Config -- [app_public_cert_path]');
throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [app_public_cert_path]');
}
$ssl = openssl_x509_parse(get_public_cert($path));

View File

@ -14,6 +14,7 @@ use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_alipay_config;
use function Yansongda\Pay\should_do_http_request;
use function Yansongda\Pay\verify_alipay_sign;
@ -42,10 +43,12 @@ class VerifySignaturePlugin implements PluginInterface
$result = $destination->except('_sign')->all();
if ('' === $sign || empty($result)) {
throw new InvalidSignException(Exception::SIGN_EMPTY, 'Verify Alipay Response Sign Failed: sign is empty', $destination);
throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 验证支付宝签名失败-支付宝签名为空', $destination);
}
verify_alipay_sign($rocket->getParams(), json_encode($result, JSON_UNESCAPED_UNICODE), $sign);
$config = get_alipay_config($rocket->getParams());
verify_alipay_sign($config, json_encode($result, JSON_UNESCAPED_UNICODE), $sign);
Logger::info('[Alipay][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]);

View File

@ -56,7 +56,7 @@ class ParserPlugin implements PluginInterface
$packer = is_string($packer) ? Pay::get($packer) : $packer;
if (!$packer instanceof PackerInterface) {
throw new InvalidConfigException(Exception::CONFIG_PACKER_INVALID);
throw new InvalidConfigException(Exception::CONFIG_PACKER_INVALID, '配置参数异常: 配置的 `PackerInterface` 未实现 `PackerInterface`');
}
return $packer;

View File

@ -37,7 +37,7 @@ class CallbackPlugin implements PluginInterface
$signature = $params['signature'] ?? false;
if (!$signature) {
throw new InvalidSignException(Exception::SIGN_EMPTY, '', $params);
throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 银联签名为空', $params);
}
verify_unipay_sign($params, $rocket->getPayload()->sortKeys()->toString(), $signature);

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Packer\JsonPacker;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\filter_params;
class AddPayloadBodyPlugin implements PluginInterface
{
protected JsonPacker $jsonPacker;
public function __construct(?JsonPacker $jsonPacker = null)
{
$this->jsonPacker = $jsonPacker ?? new JsonPacker();
}
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][AddPayloadBodyPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload(['_body' => $this->getBody($rocket->getPayload())]);
Logger::info('[Wechat][AddPayloadBodyPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
protected function getBody(?Collection $payload): string
{
$actualPayload = filter_params($payload->all());
return empty($actualPayload) ? '' : $this->jsonPacker->pack($actualPayload);
}
}

View File

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat;
use Closure;
use Throwable;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use Yansongda\Supports\Str;
use function Yansongda\Pay\get_public_cert;
use function Yansongda\Pay\get_wechat_body;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_method;
use function Yansongda\Pay\get_wechat_sign;
use function Yansongda\Pay\get_wechat_url;
class AddPayloadSignaturePlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
* @throws Throwable 随机数生成失败
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][AddPayloadSignaturePlugin] 插件开始装载', ['rocket' => $rocket]);
$config = get_wechat_config($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][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;
}
}

View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Request;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_body;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_method;
use function Yansongda\Pay\get_wechat_url;
class AddRadarPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$payload = $rocket->getPayload();
$config = get_wechat_config($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);
}
/**
* @throws InvalidParamsException
*/
protected function getHeaders(?Collection $payload): array
{
$authorization = $payload?->get('_authorization') ?? null;
if (empty($authorization)) {
throw new InvalidParamsException(Exception::PARAMS_WECHAT_AUTHORIZATION_MISSING, '参数异常: 微信 `_authorization` 参数缺失:你可能用错插件顺序,应该先使用 `AddPayloadSignaturePlugin`, 再使用 `AddRadarPlugin`');
}
$headers = [
'Accept' => 'application/json, text/plain, application/x-gzip',
'User-Agent' => 'yansongda/pay-v3',
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => $authorization,
];
// 当 body 里有加密内容时,需要传递此参数用于微信区分
if ($payload->has('_serial_no')) {
$headers['Wechatpay-Serial'] = $payload->get('_serial_no');
}
if ($payload->has('_content-type')) {
$headers['Content-Type'] = $payload->get('_content-type');
}
if ($payload->has('_accept')) {
$headers['Accept'] = $payload->get('_accept');
}
return $headers;
}
}

View File

@ -9,16 +9,18 @@ use Psr\Http\Message\ServerRequestInterface;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Direction\NoHttpRequestDirection;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\DecryptException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\InvalidResponseException;
use Yansongda\Pay\Exception\InvalidSignException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\decrypt_wechat_resource;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\verify_wechat_sign;
class CallbackPlugin implements PluginInterface
@ -26,28 +28,31 @@ class CallbackPlugin implements PluginInterface
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws ServiceNotFoundException
* @throws InvalidResponseException
* @throws InvalidParamsException
* @throws InvalidSignException
* @throws ServiceNotFoundException
* @throws DecryptException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[wechat][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]);
Logger::debug('[Wechat][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->formatRequestAndParams($rocket);
$this->init($rocket);
$params = $rocket->getParams();
/* @phpstan-ignore-next-line */
verify_wechat_sign($rocket->getDestinationOrigin(), $rocket->getParams());
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'] ?? [], $rocket->getParams());
$body['resource'] = decrypt_wechat_resource($body['resource'] ?? [], get_wechat_config($params));
$rocket->setDestination(new Collection($body));
Logger::info('[wechat][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
Logger::info('[Wechat][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
@ -55,16 +60,17 @@ class CallbackPlugin implements PluginInterface
/**
* @throws InvalidParamsException
*/
protected function formatRequestAndParams(Rocket $rocket): void
protected function init(Rocket $rocket): void
{
$request = $rocket->getParams()['request'] ?? null;
$request = $rocket->getParams()['_request'] ?? null;
$params = $rocket->getParams()['_params'] ?? [];
if (!$request instanceof ServerRequestInterface) {
throw new InvalidParamsException(Exception::PARAMS_REQUEST_EMPTY);
throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID, '参数异常: 微信回调参数不正确');
}
$rocket->setDestination(clone $request)
->setDestinationOrigin($request)
->setParams($rocket->getParams()['params'] ?? []);
->setParams($params);
}
}

View File

@ -1,55 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_1.shtml
*/
class ApplyPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE);
}
protected function getPartnerUri(Rocket $rocket): string
{
return 'v3/ecommerce/refunds/apply';
}
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$config = get_wechat_config($rocket->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);
}
}

View File

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_2.shtml
*/
class QueryPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE);
}
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getPartnerUri(Rocket $rocket): string
{
$payload = $rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_5.shtml
*/
class QueryReturnAdvancePlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE);
}
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getPartnerUri(Rocket $rocket): string
{
$payload = $rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
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);
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_4.shtml
*/
class ReturnAdvancePlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
throw new InvalidParamsException(Exception::PARAMS_PLUGIN_ONLY_SUPPORT_SERVICE_MODE);
}
/**
* @throws InvalidParamsException
*/
protected function getPartnerUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('refund_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
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'] ?? ''),
]));
}
}

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/complete-complaint-v2.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/complete-complaint-v2.html
*/
class CompletePlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][CompletePlugin] 插件开始装载', ['rocket' => $rocket]);
$config = get_wechat_config($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);
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaint-notifications/delete-complaint-notifications.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaint-notifications/delete-complaint-notifications.html
*/
class DeleteCallbackPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][DeleteCallbackPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaint-notifications/query-complaint-notifications.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaint-notifications/query-complaint-notifications.html
*/
class QueryCallbackPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][QueryCallbackPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/query-complaint-v2.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/query-complaint-v2.html
*/
class QueryDetailPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][QueryDetailPlugin] 插件开始装载', ['rocket' => $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]);
return $next($rocket);
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/images/query-images.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/images/query-images.html
*/
class QueryImagePlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][QueryImagePlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/query-negotiation-history-v2.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/query-negotiation-history-v2.html
*/
class QueryNegotiationPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][QueryNegotiationPlugin] 插件开始装载', ['rocket' => $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 = $payload->except('complaint_id')->query();
return empty($query) ? '' : '?'.$query;
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/list-complaints-v2.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/list-complaints-v2.html
*/
class QueryPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][QueryPlugin] 插件开始装载', ['rocket' => $rocket]);
$payload = $rocket->getPayload();
if (is_null($payload)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询投诉单列表,缺少必要参数');
}
$query = $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);
}
}

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/response-complaint-v2.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/response-complaint-v2.html
*/
class ResponsePlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][ResponsePlugin] 插件开始装载', ['rocket' => $rocket]);
$config = get_wechat_config($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);
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaint-notifications/create-complaint-notifications.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaint-notifications/create-complaint-notifications.html
*/
class SetCallbackPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][SetCallbackPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaint-notifications/update-complaint-notifications.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaint-notifications/update-complaint-notifications.html
*/
class UpdateCallbackPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][UpdateCallbackPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\Complaints;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/consumer-complaint/complaints/update-refund-progress.html
* @see https://pay.weixin.qq.com/docs/partner/apis/consumer-complaint/complaints/update-refund-progress.html
*/
class UpdateRefundPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][Complaints][UpdateRefundPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\DecryptException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\encrypt_wechat_contents;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_public_key;
use function Yansongda\Pay\get_wechat_serial_no;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/receivers/add-receiver.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/receivers/add-receiver.html
*/
class AddReceiverPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws DecryptException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][AddReceiverPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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);
$publicKey = get_wechat_public_key($config, $data['_serial_no']);
$data['name'] = encrypt_wechat_contents($payload->get('name'), $publicKey);
return $data;
}
}

View File

@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\DecryptException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\encrypt_wechat_contents;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_public_key;
use function Yansongda\Pay\get_wechat_serial_no;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/orders/create-order.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/orders/create-order.html
*/
class CreatePlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws DecryptException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][CreatePlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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);
$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;
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/receivers/delete-receiver.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/receivers/delete-receiver.html
*/
class DeleteReceiverPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][DeleteReceiverPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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;
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/download-bill.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/download-bill.html
*/
class DownloadBillPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][DownloadBillPlugin] 插件开始装载', ['rocket' => $rocket]);
$downloadUrl = $rocket->getPayload()?->get('download_url') ?? null;
if (empty($downloadUrl)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载电子回单,参数缺少 `download_url`');
}
$rocket->setPayload([
'_method' => 'GET',
'_url' => $downloadUrl,
'_service_url' => $downloadUrl,
]);
Logger::info('[Wechat][Extend][ProfitSharing][DownloadBillPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/transactions/query-order-amount.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/transactions/query-order-amount.html
*/
class QueryAmountsPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][QueryAmountsPlugin] 插件开始装载', ['rocket' => $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);
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/merchants/query-merchant-ratio.html
*/
class QueryMerchantConfigsPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][QueryMerchantConfigsPlugin] 插件开始装载', ['rocket' => $rocket]);
$payload = $rocket->getPayload();
$config = get_wechat_config($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);
}
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/orders/query-order.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/orders/query-order.html
*/
class QueryPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][QueryPlugin] 插件开始装载', ['rocket' => $rocket]);
$config = get_wechat_config($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);
}
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/return-orders/query-return-order.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/return-orders/query-return-order.html
*/
class QueryReturnPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][QueryReturnPlugin] 插件开始装载', ['rocket' => $rocket]);
$config = get_wechat_config($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);
}
}

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/return-orders/create-return-order.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/return-orders/create-return-order.html
*/
class ReturnPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][ReturnPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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'] ?? ''),
];
}
}

View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Extend\ProfitSharing;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/profit-sharing/orders/unfreeze-order.html
* @see https://pay.weixin.qq.com/docs/partner/apis/profit-sharing/orders/unfreeze-order.html
*/
class UnfreezePlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Extend][ProfitSharing][UnfreezePlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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),
];
}
}

View File

@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Balance;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
class QueryDayEndPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void {}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('account_type') || !$payload->has('date')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/merchant/fund/dayendbalance/'.
$payload->get('account_type').
'?date='.$payload->get('date');
}
}

View File

@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Balance;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
class QueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void {}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('account_type')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/merchant/fund/balance/'.
$payload->get('account_type');
}
}

View File

@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\InvalidResponseException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\HasWechatEncryption;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\encrypt_wechat_contents;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_8.shtml
*/
class AddReceiverPlugin extends GeneralPlugin
{
use HasWechatEncryption;
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$params = $rocket->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);
}
}

View File

@ -1,89 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\InvalidResponseException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\HasWechatEncryption;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\encrypt_wechat_contents;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_1.shtml
*/
class CreatePlugin extends GeneralPlugin
{
use HasWechatEncryption;
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$payload = $rocket->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;
}
}

View File

@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_9.shtml
*/
class DeleteReceiverPlugin extends GeneralPlugin
{
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$config = get_wechat_config($rocket->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';
}
}

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Direction\OriginResponseDirection;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_12.shtml
*/
class DownloadBillPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('download_url')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return $payload->get('download_url');
}
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setDirection(OriginResponseDirection::class);
$rocket->setPayload(null);
}
}

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_6.shtml
*/
class QueryAmountsPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('transaction_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/profitsharing/transactions/'.
$payload->get('transaction_id').
'/amounts';
}
}

View File

@ -1,49 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_7.shtml
*/
class QueryMerchantConfigsPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_METHOD_NOT_SUPPORTED);
}
return 'v3/profitsharing/merchant-configs/'.
$payload->get('sub_mchid', $config['sub_mch_id'] ?? '');
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_2.shtml
*/
class QueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$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;
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_4.shtml
*/
class QueryReturnPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$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;
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_3.shtml
*/
class ReturnPlugin extends GeneralPlugin
{
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$config = get_wechat_config($rocket->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';
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_5.shtml
*/
class UnfreezePlugin extends GeneralPlugin
{
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$payload = $rocket->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';
}
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_7.shtml
*/
class CreateBillReceiptPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function doSomething(Rocket $rocket): void
{
$payload = $rocket->getPayload();
if (!$payload->has('out_batch_no')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
}
protected function getUri(Rocket $rocket): string
{
return 'v3/transfer/bill-receipt';
}
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_9.shtml
*/
class CreateDetailReceiptPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function doSomething(Rocket $rocket): void
{
$payload = $rocket->getPayload();
if (!$payload->has('out_detail_no') || !$payload->has('accept_type')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
}
protected function getUri(Rocket $rocket): string
{
return 'v3/transfer-detail/electronic-receipts';
}
}

View File

@ -1,99 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\InvalidConfigException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\InvalidResponseException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\HasWechatEncryption;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\encrypt_wechat_contents;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml
*/
class CreatePlugin extends GeneralPlugin
{
use HasWechatEncryption;
/**
* @throws ContainerException
* @throws InvalidConfigException
* @throws InvalidParamsException
* @throws InvalidResponseException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
{
$params = $rocket->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;
}
}

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Direction\OriginResponseDirection;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_11.shtml
*/
class DownloadReceiptPlugin extends GeneralPlugin
{
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('download_url')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return $payload->get('download_url');
}
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setDirection(OriginResponseDirection::class);
$rocket->setPayload(null);
}
}

View File

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_3.shtml
*/
class QueryBatchDetailIdPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
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::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/partner-transfer/batches/batch-id/'.
$payload->get('batch_id').
'/details/detail-id/'.
$payload->get('detail_id');
}
}

View File

@ -1,64 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_2.shtml
*/
class QueryBatchIdPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$batchId = $payload->get('batch_id');
$payload->forget('batch_id');
return 'v3/partner-transfer/batches/batch-id/'.$batchId.
'?'.$payload->query();
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_8.shtml
*/
class QueryBillReceiptPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
if (!$payload->has('out_batch_no')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/transfer/bill-receipt/'.$payload->get('out_batch_no');
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_10.shtml
*/
class QueryDetailReceiptPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
/**
* @throws InvalidParamsException
*/
protected function doSomething(Rocket $rocket): void
{
$payload = $rocket->getPayload();
if (!$payload->has('out_detail_no') || !$payload->has('accept_type')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
$rocket->setPayload(null);
}
protected function getUri(Rocket $rocket): string
{
return 'v3/transfer-detail/electronic-receipts?'.$rocket->getPayload()->query();
}
}

View File

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_6.shtml
*/
class QueryOutBatchDetailNoPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
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::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/partner-transfer/batches/out-batch-no/'.
$payload->get('out_batch_no').
'/details/out-detail-no/'.
$payload->get('out_detail_no');
}
}

View File

@ -1,64 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Fund\Transfer;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_5.shtml
*/
class QueryOutBatchNoPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$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::PARAMS_NECESSARY_PARAMS_MISSING);
}
$outBatchNo = $payload->get('out_batch_no');
$payload->forget('out_batch_no');
return 'v3/partner-transfer/batches/out-batch-no/'.$outBatchNo.
'?'.$payload->query();
}
}

View File

@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat;
use Closure;
use Psr\Http\Message\RequestInterface;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Request;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_base_uri;
use function Yansongda\Pay\get_wechat_config;
abstract class GeneralPlugin implements PluginInterface
{
/**
* @throws ServiceNotFoundException
* @throws ContainerException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[wechat][GeneralPlugin] 通用插件开始装载', ['rocket' => $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 str_starts_with($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);
}
}

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Packer\XmlPacker;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
abstract class GeneralV2Plugin extends GeneralPlugin
{
protected function getHeaders(): array
{
return [
'Content-Type' => '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;
}

View File

@ -4,34 +4,42 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_1.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/create-coupon-stock.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/create-coupon-stock.html
*/
class CreatePlugin extends GeneralPlugin
class CreatePlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$config = get_wechat_config($rocket->getParams());
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][CreatePlugin] 插件开始装载', ['rocket' => $rocket]);
if (!$payload->has('belong_merchant')) {
$rocket->mergePayload(['belong_merchant' => $config['mch_id']]);
}
}
$params = $rocket->getParams();
$config = get_wechat_config($params);
$belongMerchant = $rocket->getPayload()?->get('belong_merchant') ?? $config['mch_id'];
protected function getUri(Rocket $rocket): string
{
return 'v3/marketing/favor/coupon-stocks';
$rocket->mergePayload([
'_method' => 'POST',
'_url' => 'v3/marketing/favor/coupon-stocks',
'_service_url' => 'v3/marketing/favor/coupon-stocks',
'belong_merchant' => $belongMerchant,
]);
Logger::info('[Wechat][Marketing][Coupon][CreatePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,47 +4,51 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_13.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/pause-stock.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/pause-stock.html
*/
class PausePlugin extends GeneralPlugin
class PausePlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$config = get_wechat_config($rocket->getParams());
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][PausePlugin] 插件开始装载', ['rocket' => $rocket]);
if (!$payload->has('stock_creator_mchid')) {
$rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
$params = $rocket->getParams();
$config = get_wechat_config($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->getPayload()->forget('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,
]);
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
{
$payload = $rocket->getPayload();
Logger::info('[Wechat][Marketing][Coupon][PausePlugin] 插件装载完毕', ['rocket' => $rocket]);
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
}
return 'v3/marketing/favor/stocks/'.$payload->get('stock_id').'/pause';
return $next($rocket);
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/call-back-url/query-callback.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/call-back-url/query-callback.html
*/
class QueryCallbackPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Marketing][Coupon][QueryCallbackPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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][QueryCallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,48 +4,52 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_6.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/coupon/query-coupon.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/coupon/query-coupon.html
*/
class QueryCouponDetailPlugin extends GeneralPlugin
class QueryCouponDetailPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryCouponDetailPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($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 (!$payload->has('coupon_id') || !$payload->has('openid')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($openId) || empty($couponId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券详情,参数缺少 `openid` 或 `coupon_id`');
}
return 'v3/marketing/favor/users/'.
$payload->get('openid').
'/coupons/'.$payload->get('coupon_id').
'?appid='.$payload->get('appid', $config[$this->getConfigKey($params)] ?? '');
$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][Marketing][Coupon][QueryCouponDetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,47 +4,50 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_5.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/query-stock.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/query-stock.html
*/
class QueryStockDetailPlugin extends GeneralPlugin
class QueryStockDetailPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryStockDetailPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
$stockId = $payload?->get('stock_id') ?? null;
$mchId = $payload?->get('stock_creator_mchid') ?? $config['mch_id'] ?? 'null';
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券批次详情,参数缺少 `stock_id`');
}
return 'v3/marketing/favor/stocks/'.
$payload->get('stock_id').
'?stock_creator_mchid='.$payload->get('stock_creator_mchid', $config['mch_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][Marketing][Coupon][QueryStockDetailPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,55 +4,61 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_8.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/list-available-singleitems.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/list-available-singleitems.html
*/
class QueryStockItemsPlugin extends GeneralPlugin
class QueryStockItemsPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryStockItemsPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
$stockId = $payload?->get('stock_id') ?? null;
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券可用单品,参数缺少 `stock_id`');
}
if (!$payload->has('stock_creator_mchid')) {
$rocket->mergePayload(['stock_creator_mchid' => $config['mch_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][Marketing][Coupon][QueryStockItemsPlugin] 插件装载完毕', ['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'] ?? '');
}
$query = $rocket->getPayload()->all();
unset($query['stock_id']);
return 'v3/marketing/favor/stocks/'.
$payload->get('stock_id').
'/items?'.http_build_query($query);
return $payload->except('stock_id')->query();
}
}

View File

@ -4,55 +4,61 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_7.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/list-available-merchants.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/list-available-merchants.html
*/
class QueryStockMerchantsPlugin extends GeneralPlugin
class QueryStockMerchantsPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryStockMerchantsPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
$stockId = $payload?->get('stock_id') ?? null;
if (!$payload->has('stock_creator_mchid')) {
$rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 查询代金券可用商户,参数缺少 `stock_id`');
}
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
$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][Marketing][Coupon][QueryStockMerchantsPlugin] 插件装载完毕', ['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'] ?? '');
}
$query = $rocket->getPayload()->all();
unset($query['stock_id']);
return 'v3/marketing/favor/stocks/'.
$payload->get('stock_id').
'/merchants?'.http_build_query($query);
return $payload->except('stock_id')->query();
}
}

View File

@ -4,39 +4,40 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_11.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/refund-flow.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/refund-flow.html
*/
class QueryStockRefundFlowPlugin extends GeneralPlugin
class QueryStockRefundFlowPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryStockRefundFlowPlugin] 插件开始装载', ['rocket' => $rocket]);
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
$stockId = $rocket->getPayload()?->get('stock_id') ?? null;
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载批次退款明细,参数缺少 `stock_id`');
}
return 'v3/marketing/favor/stocks/'.
$payload->get('stock_id').
'/refund-flow';
$rocket->setPayload([
'_method' => 'GET',
'_url' => 'v3/marketing/favor/stocks/'.$stockId.'/refund-flow',
'_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/refund-flow',
]);
Logger::info('[Wechat][Marketing][Coupon][QueryStockRefundFlowPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,39 +4,40 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_10.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/use-flow.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/use-flow.html
*/
class QueryStockUseFlowPlugin extends GeneralPlugin
class QueryStockUseFlowPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryStockUseFlowPlugin] 插件开始装载', ['rocket' => $rocket]);
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
$stockId = $rocket->getPayload()?->get('stock_id') ?? null;
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 下载批次核销明细,参数缺少 `stock_id`');
}
return 'v3/marketing/favor/stocks/'.
$payload->get('stock_id').
'/use-flow';
$rocket->setPayload([
'_method' => 'GET',
'_url' => 'v3/marketing/favor/stocks/'.$stockId.'/use-flow',
'_service_url' => 'v3/marketing/favor/stocks/'.$stockId.'/use-flow',
]);
Logger::info('[Wechat][Marketing][Coupon][QueryStockUseFlowPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,41 +4,60 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_4.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/list-stocks.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/list-stocks.html
*/
class QueryStocksPlugin extends GeneralPlugin
class QueryStocksPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Marketing][Coupon][QueryStocksPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
if (!$rocket->getPayload()->has('stock_creator_mchid')) {
$rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
if (is_null($payload)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少代金券相关参数');
}
return 'v3/marketing/favor/stocks?'.$rocket->getPayload()->query();
$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][Marketing][Coupon][QueryStocksPlugin] 插件装载完毕', ['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 $payload->query();
}
}

View File

@ -4,59 +4,62 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_9.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/coupon/list-coupons-by-filter.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/coupon/list-coupons-by-filter.html
*/
class QueryUserCouponsPlugin extends GeneralPlugin
class QueryUserCouponsPlugin implements PluginInterface
{
protected function getMethod(): string
{
return 'GET';
}
protected function doSomething(Rocket $rocket): void
{
$rocket->setPayload(null);
}
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function getUri(Rocket $rocket): string
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][QueryUserCouponsPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
$openId = $payload?->get('openid') ?? null;
if (!$payload->has('openid')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($openId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 根据商户号查用户的券,参数缺少 `openid`');
}
if (!$payload->has('appid')) {
$rocket->mergePayload(['appid' => $config[$this->getConfigKey($params)] ?? '']);
$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][Marketing][Coupon][QueryUserCouponsPlugin] 插件装载完毕', ['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)] ?? '');
}
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);
return $payload->except('openid')->query();
}
}

View File

@ -4,48 +4,51 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use function Yansongda\Pay\get_wechat_config;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_14.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/stock/restart-stock.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/stock/restart-stock.html
*/
class RestartPlugin extends GeneralPlugin
class RestartPlugin implements PluginInterface
{
/**
* @throws ContainerException
* @throws InvalidParamsException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$payload = $rocket->getPayload();
Logger::debug('[Wechat][Marketing][Coupon][RestartPlugin] 插件开始装载', ['rocket' => $rocket]);
$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();
$stockId = $payload?->get('stock_id') ?? null;
$stockCreatorMchId = $payload?->get('stock_creator_mchid') ?? $config['mch_id'] ?? '';
if (!$payload->has('stock_id')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($stockId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 激活代金券,参数缺少 `stock_id`');
}
return 'v3/marketing/favor/stocks/'.$payload->get('stock_id').'/restart';
$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][Marketing][Coupon][RestartPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@ -4,51 +4,67 @@ declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Wechat\Marketing\Coupon;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Plugin\Wechat\GeneralPlugin;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use function Yansongda\Pay\get_wechat_config;
use function Yansongda\Pay\get_wechat_type_key;
/**
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_2.shtml
* @see https://pay.weixin.qq.com/docs/merchant/apis/cash-coupons/coupon/send-coupon.html
* @see https://pay.weixin.qq.com/docs/partner/apis/cash-coupons/coupon/send-coupon.html
*/
class SendPlugin extends GeneralPlugin
class SendPlugin implements PluginInterface
{
/**
* @throws InvalidParamsException
* @throws ContainerException
* @throws ServiceNotFoundException
*/
protected function doSomething(Rocket $rocket): void
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][Marketing][Coupon][SendPlugin] 插件开始装载', ['rocket' => $rocket]);
$params = $rocket->getParams();
$config = get_wechat_config($params);
$payload = $rocket->getPayload();
$openId = $payload?->get('openid') ?? null;
if (!$rocket->getPayload()->has('appid')) {
$rocket->mergePayload(['appid' => $config[$this->getConfigKey($params)] ?? '']);
if (empty($openId)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 发放指定批次的代金券,参数缺少 `openid`');
}
if (!$rocket->getPayload()->has('stock_creator_mchid')) {
$rocket->mergePayload(['stock_creator_mchid' => $config['mch_id']]);
}
$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),
));
$rocket->getPayload()->forget('openid');
Logger::info('[Wechat][Marketing][Coupon][SendPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
/**
* @throws InvalidParamsException
*/
protected function getUri(Rocket $rocket): string
protected function normal(Collection $payload, array $params, array $config): array
{
$payload = $rocket->getPayload();
if (!$payload->has('openid')) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
if (empty($payload->get('appid'))) {
$payload->set('appid', $config[get_wechat_type_key($params)] ?? '');
}
return 'v3/marketing/favor/users/'.$payload->get('openid').'/coupons';
if (empty($payload->get('stock_creator_mchid'))) {
$payload->set('stock_creator_mchid', $config['mch_id'] ?? '');
}
return $payload->except('openid')->all();
}
}

Some files were not shown because too many files have changed in this diff Show More