mirror of
https://gitee.com/yansongda/pay.git
synced 2024-11-29 18:58:38 +08:00
refactor: 优化支付宝 launch 插件代码 (#678)
* deprecated: 支付宝/微信 `RadarPlugin`, `SignPlugin` 已废弃,使用 `RadarSignPlugin` 代替
This commit is contained in:
parent
ebc939e1d7
commit
9412db512f
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
||||
## v3.2.2
|
||||
|
||||
### refactor
|
||||
|
||||
- refactor: 优化支付宝 launch 插件代码(#678)
|
||||
|
||||
### deprecated
|
||||
|
||||
- deprecated: 支付宝 `RadarPlugin`, `SignPlugin` 已废弃,使用 `RadarSignPlugin` 代替(#678)
|
||||
- deprecated: 微信 `SignPlugin` 已废弃,使用 `RadarSignPlugin` 代替(#678)
|
||||
|
||||
## v3.2.1
|
||||
|
||||
### fixed
|
||||
|
@ -1,5 +1,7 @@
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
excludePaths:
|
||||
- src/Service/ContainerServiceProvider.php
|
||||
ignoreErrors:
|
||||
- '#.* Illuminate\\Container\\Container.*#'
|
||||
- '#.* Hyperf\\Utils\\ApplicationContext.*#'
|
||||
- '#.* think\\Container.*#'
|
||||
|
@ -69,8 +69,6 @@ if (!function_exists('get_private_cert')) {
|
||||
|
||||
if (!function_exists('verify_alipay_sign')) {
|
||||
/**
|
||||
* @param string $sign base64decode 之后的
|
||||
*
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
@ -86,7 +84,7 @@ if (!function_exists('verify_alipay_sign')) {
|
||||
|
||||
$result = 1 === openssl_verify(
|
||||
$contents,
|
||||
$sign,
|
||||
base64_decode($sign),
|
||||
get_public_cert($public),
|
||||
OPENSSL_ALGO_SHA256);
|
||||
|
||||
|
@ -110,6 +110,8 @@ class Pay
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
@ -119,8 +121,8 @@ class Pay
|
||||
try {
|
||||
$container = Pay::getContainer();
|
||||
|
||||
if ($container instanceof LaravelContainer) { // @phpstan-ignore-line
|
||||
$container->singleton($name, $value instanceof Closure ? $value : static fn () => $value); // @phpstan-ignore-line
|
||||
if ($container instanceof LaravelContainer) {
|
||||
$container->singleton($name, $value instanceof Closure ? $value : static fn () => $value);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -140,6 +142,8 @@ class Pay
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
|
@ -30,12 +30,13 @@ class CallbackPlugin implements PluginInterface
|
||||
Logger::info('[alipay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]);
|
||||
|
||||
$this->formatPayload($rocket);
|
||||
$sign = $rocket->getParams()['sign'] ?? false;
|
||||
|
||||
if (!($rocket->getParams()['sign'] ?? false)) {
|
||||
if (!$sign) {
|
||||
throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', $rocket->getParams());
|
||||
}
|
||||
|
||||
verify_alipay_sign($rocket->getParams(), $this->getSignContent($rocket->getPayload()), base64_decode($rocket->getParams()['sign']));
|
||||
verify_alipay_sign($rocket->getParams(), $this->getSignContent($rocket->getPayload()), $sign);
|
||||
|
||||
$rocket->setDirection(NoHttpRequestParser::class)
|
||||
->setDestination($rocket->getPayload());
|
||||
|
@ -32,13 +32,12 @@ class LaunchPlugin implements PluginInterface
|
||||
Logger::info('[alipay][LaunchPlugin] 插件开始装载', ['rocket' => $rocket]);
|
||||
|
||||
if (should_do_http_request($rocket->getDirection())) {
|
||||
$this->verifySign($rocket);
|
||||
$response = Collection::wrap($rocket->getDestination());
|
||||
$result = $response->get($this->getResultKey($rocket->getPayload()));
|
||||
|
||||
$rocket->setDestination(
|
||||
Collection::wrap(
|
||||
$rocket->getDestination()->get($this->getResultKey($rocket->getPayload()))
|
||||
)
|
||||
);
|
||||
$this->verifySign($rocket->getParams(), $response, $result);
|
||||
|
||||
$rocket->setDestination(Collection::wrap($result));
|
||||
}
|
||||
|
||||
Logger::info('[alipay][LaunchPlugin] 插件装载完毕', ['rocket' => $rocket]);
|
||||
@ -52,17 +51,15 @@ class LaunchPlugin implements PluginInterface
|
||||
* @throws \Yansongda\Pay\Exception\InvalidResponseException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function verifySign(Rocket $rocket): void
|
||||
protected function verifySign(array $params, Collection $response, ?array $result): void
|
||||
{
|
||||
$response = $rocket->getDestination();
|
||||
$result = $response->get($this->getResultKey($rocket->getPayload()));
|
||||
$sign = $response->get('sign', '');
|
||||
|
||||
if ('' === $sign || is_null($result)) {
|
||||
throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, 'Verify Alipay Response Sign Failed: sign is empty', $response);
|
||||
}
|
||||
|
||||
verify_alipay_sign($rocket->getParams(), json_encode($result, JSON_UNESCAPED_UNICODE), base64_decode($sign));
|
||||
verify_alipay_sign($params, json_encode($result, JSON_UNESCAPED_UNICODE), $sign);
|
||||
}
|
||||
|
||||
protected function getResultKey(Collection $payload): string
|
||||
|
@ -16,6 +16,9 @@ use Yansongda\Pay\Provider\Alipay;
|
||||
use Yansongda\Pay\Request;
|
||||
use Yansongda\Pay\Rocket;
|
||||
|
||||
/**
|
||||
* @deprecated use RadarSignPlugin instead
|
||||
*/
|
||||
class RadarPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
|
143
src/Plugin/Alipay/RadarSignPlugin.php
Normal file
143
src/Plugin/Alipay/RadarSignPlugin.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Yansongda\Pay\Plugin\Alipay;
|
||||
|
||||
use Closure;
|
||||
use Yansongda\Pay\Contract\PluginInterface;
|
||||
use Yansongda\Pay\Exception\Exception;
|
||||
use Yansongda\Pay\Exception\InvalidConfigException;
|
||||
|
||||
use function Yansongda\Pay\get_alipay_config;
|
||||
use function Yansongda\Pay\get_private_cert;
|
||||
|
||||
use Yansongda\Pay\Logger;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Provider\Alipay;
|
||||
use Yansongda\Pay\Request;
|
||||
use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Supports\Collection;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
class RadarSignPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
public function assembly(Rocket $rocket, Closure $next): Rocket
|
||||
{
|
||||
Logger::info('[alipay][RadarSignPlugin] 插件开始装载', ['rocket' => $rocket]);
|
||||
|
||||
$this->sign($rocket);
|
||||
|
||||
$this->reRadar($rocket);
|
||||
|
||||
Logger::info('[alipay][RadarSignPlugin] 插件装载完毕', ['rocket' => $rocket]);
|
||||
|
||||
return $next($rocket);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function sign(Rocket $rocket): void
|
||||
{
|
||||
$this->formatPayload($rocket);
|
||||
|
||||
$sign = $this->getSign($rocket);
|
||||
|
||||
$rocket->mergePayload(['sign' => $sign]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function reRadar(Rocket $rocket): void
|
||||
{
|
||||
$params = $rocket->getParams();
|
||||
|
||||
$rocket->setRadar(new Request(
|
||||
$this->getMethod($params),
|
||||
$this->getUrl($params),
|
||||
$this->getHeaders(),
|
||||
$this->getBody($rocket->getPayload()),
|
||||
));
|
||||
}
|
||||
|
||||
protected function formatPayload(Rocket $rocket): void
|
||||
{
|
||||
$payload = $rocket->getPayload()->filter(fn ($v, $k) => '' !== $v && !is_null($v) && 'sign' != $k);
|
||||
|
||||
$contents = array_filter($payload->get('biz_content', []), fn ($v, $k) => !Str::startsWith(strval($k), '_'), ARRAY_FILTER_USE_BOTH);
|
||||
|
||||
$rocket->setPayload(
|
||||
$payload->merge(['biz_content' => json_encode($contents)])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function getSign(Rocket $rocket): string
|
||||
{
|
||||
$privateKey = $this->getPrivateKey($rocket->getParams());
|
||||
|
||||
$content = $rocket->getPayload()->sortKeys()->toString();
|
||||
|
||||
openssl_sign($content, $sign, $privateKey, OPENSSL_ALGO_SHA256);
|
||||
|
||||
return base64_encode($sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function getPrivateKey(array $params): string
|
||||
{
|
||||
$privateKey = get_alipay_config($params)['app_secret_cert'] ?? null;
|
||||
|
||||
if (is_null($privateKey)) {
|
||||
throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_secret_cert]');
|
||||
}
|
||||
|
||||
return get_private_cert($privateKey);
|
||||
}
|
||||
|
||||
protected function getMethod(array $params): string
|
||||
{
|
||||
return strtoupper($params['_method'] ?? 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function getUrl(array $params): string
|
||||
{
|
||||
$config = get_alipay_config($params);
|
||||
|
||||
return Alipay::URL[$config['mode'] ?? Pay::MODE_NORMAL];
|
||||
}
|
||||
|
||||
protected function getHeaders(): array
|
||||
{
|
||||
return [
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
];
|
||||
}
|
||||
|
||||
protected function getBody(Collection $payload): string
|
||||
{
|
||||
return $payload->query();
|
||||
}
|
||||
}
|
@ -16,6 +16,9 @@ use Yansongda\Pay\Logger;
|
||||
use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
/**
|
||||
* @deprecated use RadarSignPlugin instead
|
||||
*/
|
||||
class SignPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
|
114
src/Plugin/Wechat/RadarSignPlugin.php
Normal file
114
src/Plugin/Wechat/RadarSignPlugin.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Yansongda\Pay\Plugin\Wechat;
|
||||
|
||||
use Closure;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use Yansongda\Pay\Contract\PluginInterface;
|
||||
use Yansongda\Pay\Exception\Exception;
|
||||
use Yansongda\Pay\Exception\InvalidConfigException;
|
||||
use Yansongda\Pay\Exception\InvalidParamsException;
|
||||
|
||||
use function Yansongda\Pay\get_public_cert;
|
||||
use function Yansongda\Pay\get_wechat_config;
|
||||
use function Yansongda\Pay\get_wechat_sign;
|
||||
|
||||
use Yansongda\Pay\Logger;
|
||||
use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Supports\Collection;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
class RadarSignPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidParamsException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function assembly(Rocket $rocket, Closure $next): Rocket
|
||||
{
|
||||
Logger::info('[wechat][SignPlugin] 插件开始装载', ['rocket' => $rocket]);
|
||||
|
||||
$timestamp = time();
|
||||
$random = Str::random(32);
|
||||
$body = $this->payloadToString($rocket->getPayload());
|
||||
$contents = $this->getContents($rocket, $timestamp, $random);
|
||||
$authorization = $this->getWechatAuthorization($rocket->getParams(), $timestamp, $random, $contents);
|
||||
$radar = $rocket->getRadar()->withHeader('Authorization', $authorization);
|
||||
|
||||
if (!empty($rocket->getParams()['_serial_no'])) {
|
||||
$radar = $radar->withHeader('Wechatpay-Serial', $rocket->getParams()['_serial_no']);
|
||||
}
|
||||
|
||||
if (!empty($body)) {
|
||||
$radar = $radar->withBody(Utils::streamFor($body));
|
||||
}
|
||||
|
||||
$rocket->setRadar($radar);
|
||||
|
||||
Logger::info('[wechat][SignPlugin] 插件装载完毕', ['rocket' => $rocket]);
|
||||
|
||||
return $next($rocket);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\ContainerException
|
||||
* @throws \Yansongda\Pay\Exception\InvalidConfigException
|
||||
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
|
||||
*/
|
||||
protected function getWechatAuthorization(array $params, int $timestamp, string $random, string $contents): string
|
||||
{
|
||||
$config = get_wechat_config($params);
|
||||
$mchPublicCertPath = $config['mch_public_cert_path'] ?? null;
|
||||
|
||||
if (empty($mchPublicCertPath)) {
|
||||
throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_public_cert_path]');
|
||||
}
|
||||
|
||||
$ssl = openssl_x509_parse(get_public_cert($mchPublicCertPath));
|
||||
|
||||
if (empty($ssl['serialNumberHex'])) {
|
||||
throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Parse [mch_public_cert_path] Serial Number Error');
|
||||
}
|
||||
|
||||
$auth = sprintf(
|
||||
'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
|
||||
$config['mch_id'] ?? '',
|
||||
$random,
|
||||
$timestamp,
|
||||
$ssl['serialNumberHex'],
|
||||
get_wechat_sign($params, $contents),
|
||||
);
|
||||
|
||||
return 'WECHATPAY2-SHA256-RSA2048 '.$auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Yansongda\Pay\Exception\InvalidParamsException
|
||||
*/
|
||||
protected function getContents(Rocket $rocket, int $timestamp, string $random): string
|
||||
{
|
||||
$request = $rocket->getRadar();
|
||||
|
||||
if (is_null($request)) {
|
||||
throw new InvalidParamsException(Exception::REQUEST_NULL_ERROR);
|
||||
}
|
||||
|
||||
$uri = $request->getUri();
|
||||
|
||||
return $request->getMethod()."\n".
|
||||
$uri->getPath().(empty($uri->getQuery()) ? '' : '?'.$uri->getQuery())."\n".
|
||||
$timestamp."\n".
|
||||
$random."\n".
|
||||
$this->payloadToString($rocket->getPayload())."\n";
|
||||
}
|
||||
|
||||
protected function payloadToString(?Collection $payload): string
|
||||
{
|
||||
return (is_null($payload) || 0 === $payload->count()) ? '' : $payload->toJson();
|
||||
}
|
||||
}
|
@ -20,6 +20,9 @@ use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Supports\Collection;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
/**
|
||||
* @deprecated use RadarSignPlugin instead
|
||||
*/
|
||||
class SignPlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
|
@ -17,6 +17,7 @@ use Yansongda\Pay\Exception\InvalidConfigException;
|
||||
use Yansongda\Pay\Exception\InvalidParamsException;
|
||||
use Yansongda\Pay\Exception\InvalidResponseException;
|
||||
use Yansongda\Pay\Logger;
|
||||
use Yansongda\Pay\Parser\ArrayParser;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Rocket;
|
||||
|
||||
@ -74,7 +75,13 @@ abstract class AbstractProvider implements ProviderInterface
|
||||
|
||||
Event::dispatch(new Event\PayFinish($rocket));
|
||||
|
||||
return $rocket->getDestination();
|
||||
$destination = $rocket->getDestination();
|
||||
|
||||
if (ArrayParser::class === $rocket->getDirection() && $destination instanceof Collection) {
|
||||
return $destination->toArray();
|
||||
}
|
||||
|
||||
return $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,8 +13,7 @@ use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Plugin\Alipay\CallbackPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\LaunchPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\PreparePlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\RadarPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\SignPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\RadarSignPlugin;
|
||||
use Yansongda\Pay\Plugin\ParserPlugin;
|
||||
use Yansongda\Supports\Collection;
|
||||
use Yansongda\Supports\Str;
|
||||
@ -146,7 +145,7 @@ class Alipay extends AbstractProvider
|
||||
return array_merge(
|
||||
[PreparePlugin::class],
|
||||
$plugins,
|
||||
[SignPlugin::class, RadarPlugin::class],
|
||||
[RadarSignPlugin::class],
|
||||
[LaunchPlugin::class, ParserPlugin::class],
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use Yansongda\Pay\Plugin\ParserPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\CallbackPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\LaunchPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\PreparePlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\SignPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\RadarSignPlugin;
|
||||
use Yansongda\Supports\Collection;
|
||||
use Yansongda\Supports\Str;
|
||||
|
||||
@ -143,7 +143,7 @@ class Wechat extends AbstractProvider
|
||||
return array_merge(
|
||||
[PreparePlugin::class],
|
||||
$plugins,
|
||||
[SignPlugin::class],
|
||||
[RadarSignPlugin::class],
|
||||
[LaunchPlugin::class, ParserPlugin::class],
|
||||
);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ use Yansongda\Pay\Exception\ContainerNotFoundException;
|
||||
use Yansongda\Pay\Exception\Exception;
|
||||
use Yansongda\Pay\Pay;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ContainerServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
private $detectApplication = [
|
||||
|
@ -107,7 +107,7 @@ class FunctionTest extends TestCase
|
||||
"total_amount" => "0.01",
|
||||
"trade_no" => "2021060622001498120501382075",
|
||||
"trade_status" => "TRADE_SUCCESS",
|
||||
], JSON_UNESCAPED_UNICODE), base64_decode('Ipp1M3pwUFJ19Tx/D+40RZstXr3VSZzGxPB1Qfj1e837UkGxOJxFFK6EZ288SeEh06dPFd4qJ7BHfP/7mvkRqF1/mezBGvhBz03XTXfDn/O6IkoA+cVwpfm+i8MFvzC/ZQB0dgtZppu5qfzVyFaaNu8ct3L/NSQCMR1RXg2lH3HiwfxmIF35+LmCoL7ZPvTxB/epm7A/XNhAjLpK5GlJffPA0qwhhtQwaIZ7DHMXo06z03fbgxlBu2eEclQUm6Fobgj3JEERWLA0MDQiV1EYNWuHSSlHCMrIxWHba+Euu0jVkKKe0IFKsU8xJQbc7GTJXx/o0NfHqGwwq8hMvtgBkg=='));
|
||||
], JSON_UNESCAPED_UNICODE), 'Ipp1M3pwUFJ19Tx/D+40RZstXr3VSZzGxPB1Qfj1e837UkGxOJxFFK6EZ288SeEh06dPFd4qJ7BHfP/7mvkRqF1/mezBGvhBz03XTXfDn/O6IkoA+cVwpfm+i8MFvzC/ZQB0dgtZppu5qfzVyFaaNu8ct3L/NSQCMR1RXg2lH3HiwfxmIF35+LmCoL7ZPvTxB/epm7A/XNhAjLpK5GlJffPA0qwhhtQwaIZ7DHMXo06z03fbgxlBu2eEclQUm6Fobgj3JEERWLA0MDQiV1EYNWuHSSlHCMrIxWHba+Euu0jVkKKe0IFKsU8xJQbc7GTJXx/o0NfHqGwwq8hMvtgBkg==');
|
||||
self::assertTrue(true);
|
||||
|
||||
// test config error
|
||||
|
91
tests/Plugin/Alipay/RadarSignPluginTest.php
Normal file
91
tests/Plugin/Alipay/RadarSignPluginTest.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Yansongda\Pay\Tests\Plugin\Alipay;
|
||||
|
||||
use Yansongda\Pay\Plugin\Alipay\RadarSignPlugin;
|
||||
use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Pay\Tests\TestCase;
|
||||
use Yansongda\Supports\Collection;
|
||||
|
||||
class RadarSignPluginTest extends TestCase
|
||||
{
|
||||
protected $plugin;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->plugin = new RadarSignPlugin();
|
||||
}
|
||||
|
||||
public function testSignNormal()
|
||||
{
|
||||
$payload = [
|
||||
"app_id" => "2016082000295641",
|
||||
"method" => "alipay.trade.query",
|
||||
"format" => "JSON",
|
||||
"return_url" => "http://127.0.0.1:8000/alipay/verify",
|
||||
"charset" => "utf-8",
|
||||
"sign_type" => "RSA2",
|
||||
"timestamp" => "2021-06-07 21:54:50",
|
||||
"version" => "1.0",
|
||||
"app_cert_sn" => "fb5e86cfb784de936dd3594e32381cf8",
|
||||
"alipay_root_cert_sn" => "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6",
|
||||
"biz_content" => ['out_trade_no' => "yansongda-1622986519"],
|
||||
];
|
||||
$sign = "QMh6CzKWIt5yIYCrYrMdC2/Mt+4lTNEaPN0biIZPuiWzgTS7pyIYFOmb+dEi70X5q9UaCBlejwwwTEzRtfIjudPu/mIrlpnwsN8mEhDjyZihmgb/wCZy+kR0OIwvZjTd/3AuALIcwDbhZqDwssZAOTlco4eE7WosEdsob52OfCBAn0ZEf/9zZk5+FSbL8xbwO9hTlspl5ArgFBf9RryBxAviC09Nr5eSNdwYBIyOUdLtEVHBuHHvwa4UfiCFe0SyDFVQODgoz3Mjcs5d4RmJqKNLorkN8dHzjzlAnCR07EHsMzV4ivNG703hReHMyazPDuaWBg11/spMJUNUF/tEBQ==";
|
||||
|
||||
$rocket = new Rocket();
|
||||
$rocket->setParams([])->setPayload(new Collection($payload));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
|
||||
self::assertSame($sign, $result->getPayload()->get('sign'));
|
||||
}
|
||||
|
||||
public function testSignUnderlineParams()
|
||||
{
|
||||
$payload = [
|
||||
"app_id" => "2016082000295641",
|
||||
"method" => "alipay.trade.query",
|
||||
"format" => "JSON",
|
||||
"return_url" => "http://127.0.0.1:8000/alipay/verify",
|
||||
"charset" => "utf-8",
|
||||
"sign_type" => "RSA2",
|
||||
"timestamp" => "2021-06-07 21:54:50",
|
||||
"version" => "1.0",
|
||||
"app_cert_sn" => "fb5e86cfb784de936dd3594e32381cf8",
|
||||
"alipay_root_cert_sn" => "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6",
|
||||
"biz_content" => ['out_trade_no' => "yansongda-1622986519", '_method' => 'get', '_ignore' => true],
|
||||
];
|
||||
$sign = "QMh6CzKWIt5yIYCrYrMdC2/Mt+4lTNEaPN0biIZPuiWzgTS7pyIYFOmb+dEi70X5q9UaCBlejwwwTEzRtfIjudPu/mIrlpnwsN8mEhDjyZihmgb/wCZy+kR0OIwvZjTd/3AuALIcwDbhZqDwssZAOTlco4eE7WosEdsob52OfCBAn0ZEf/9zZk5+FSbL8xbwO9hTlspl5ArgFBf9RryBxAviC09Nr5eSNdwYBIyOUdLtEVHBuHHvwa4UfiCFe0SyDFVQODgoz3Mjcs5d4RmJqKNLorkN8dHzjzlAnCR07EHsMzV4ivNG703hReHMyazPDuaWBg11/spMJUNUF/tEBQ==";
|
||||
|
||||
$rocket = new Rocket();
|
||||
$rocket->setParams([])->setPayload(new Collection($payload));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
|
||||
self::assertSame($sign, $result->getPayload()->get('sign'));
|
||||
}
|
||||
|
||||
public function testRadarPostNormal()
|
||||
{
|
||||
$rocket = new Rocket();
|
||||
$rocket->setParams([])->setPayload(new Collection(['name' => 'yansongda']));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
|
||||
self::stringContains('name=yansongda', (string) $result->getRadar()->getBody());
|
||||
self::assertEquals('POST', $result->getRadar()->getMethod());
|
||||
}
|
||||
|
||||
public function testRadarGetNormal()
|
||||
{
|
||||
$rocket = new Rocket();
|
||||
$rocket->setParams(['_method' => 'get'])->setPayload(new Collection(['name' => 'yansongda']));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
|
||||
self::assertEquals('GET', $result->getRadar()->getMethod());
|
||||
}
|
||||
}
|
157
tests/Plugin/Wechat/RadarSignPluginTest.php
Normal file
157
tests/Plugin/Wechat/RadarSignPluginTest.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Yansongda\Pay\Tests\Plugin\Wechat;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use ReflectionClass;
|
||||
use Yansongda\Pay\Contract\ConfigInterface;
|
||||
use Yansongda\Pay\Exception\Exception;
|
||||
use Yansongda\Pay\Exception\InvalidConfigException;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Plugin\Wechat\RadarSignPlugin;
|
||||
use Yansongda\Pay\Rocket;
|
||||
use Yansongda\Pay\Tests\TestCase;
|
||||
use Yansongda\Supports\Collection;
|
||||
|
||||
class RadarSignPluginTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Yansongda\Pay\Plugin\Wechat\RadarSignPlugin
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->plugin = new RadarSignPlugin();
|
||||
}
|
||||
|
||||
public function testNormal()
|
||||
{
|
||||
$params = [
|
||||
'name' => 'yansongda',
|
||||
'age' => 28,
|
||||
];
|
||||
$rocket = (new Rocket())->setParams($params)
|
||||
->setPayload(new Collection($params))
|
||||
->setRadar(new Request('GET', '127.0.0.1'));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
$radar = $result->getRadar();
|
||||
|
||||
self::assertTrue($radar->hasHeader('Authorization'));
|
||||
self::assertFalse($radar->hasHeader('Wechatpay-Serial'));
|
||||
self::assertEquals(json_encode($params), $radar->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testNormalWithWechatSerial()
|
||||
{
|
||||
$params = [
|
||||
'_serial_no' => 'yansongda',
|
||||
'name' => 'yansongda',
|
||||
'age' => 28,
|
||||
];
|
||||
$rocket = (new Rocket())->setParams($params)
|
||||
->setPayload(new Collection($params))
|
||||
->setRadar(new Request('GET', '127.0.0.1'));
|
||||
|
||||
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
|
||||
$radar = $result->getRadar();
|
||||
|
||||
self::assertTrue($radar->hasHeader('Authorization'));
|
||||
self::assertTrue($radar->hasHeader('Wechatpay-Serial'));
|
||||
}
|
||||
|
||||
public function testGetWechatAuthorization()
|
||||
{
|
||||
$params = [
|
||||
'out_trade_no' => 1626493236,
|
||||
'description' => 'yansongda 测试 - 1626493236',
|
||||
'amount' => [
|
||||
'total' => 1,
|
||||
],
|
||||
'scene_info' => [
|
||||
'payer_client_ip' => '127.0.0.1',
|
||||
'h5_info' => [
|
||||
'type' => 'Wap',
|
||||
]
|
||||
]];
|
||||
$timestamp = 1626493236;
|
||||
$random = 'QqtzdVzxavZeXag9G5mtfzbfzFMf89p6';
|
||||
$contents = "POST\n/v3/pay/transactions/h5\n1626493236\nQqtzdVzxavZeXag9G5mtfzbfzFMf89p6\n{\"out_trade_no\":1626493236,\"description\":\"yansongda 测试 - 1626493236\",\"amount\":{\"total\":1},\"scene_info\":{\"payer_client_ip\":\"127.0.0.1\",\"h5_info\":{\"type\":\"Wap\"}},\"appid\":\"wx55955316af4ef13\",\"mchid\":\"1600314069\",\"notify_url\":\"http:\/\/127.0.0.1:8000\/wechat\/notify\"}\n";
|
||||
|
||||
$class = new ReflectionClass($this->plugin);
|
||||
$method = $class->getMethod('getWechatAuthorization');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invokeArgs($this->plugin, [$params, $timestamp, $random, $contents]);
|
||||
|
||||
self::assertEquals(
|
||||
'WECHATPAY2-SHA256-RSA2048 mchid="1600314069",nonce_str="QqtzdVzxavZeXag9G5mtfzbfzFMf89p6",timestamp="1626493236",serial_no="25F8AA5452D55497C24BA57DC81B1E5915DC2E77",signature="KzIgMgiop3nQJNdBVR2Xah/JUwVBLDFFajyXPiSN8b8YAYEA4FuWfaCgFJ52+WFed+PhOYWx/ZPih4RaEuuSdYB8eZwYUx7RZGMQZk0bKCctAjjPuf4pJN+f/WsXKjPIy3diqF5x7gyxwSCaKWP4/KjsHNqgQpiC8q1uC5xmElzuhzSwj88LIoLtkAuSmtUVvdAt0Nz41ECHZgHWSGR32TfBo902r8afdaVKkFde8IoqcEJJcp6sMxdDO5l9R5KEWxrJ1SjsXVrb0IPH8Nj7e6hfhq7pucxojPpzsC+ZWAYvufZkAQx3kTiFmY87T+QhkP9FesOfWvkIRL4E6MP6ug=="',
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetWechatAuthorizationMissingMchPublicCert()
|
||||
{
|
||||
$params = [
|
||||
'out_trade_no' => 1626493236,
|
||||
'description' => 'yansongda 测试 - 1626493236',
|
||||
'amount' => [
|
||||
'total' => 1,
|
||||
],
|
||||
'scene_info' => [
|
||||
'payer_client_ip' => '127.0.0.1',
|
||||
'h5_info' => [
|
||||
'type' => 'Wap',
|
||||
]
|
||||
]];
|
||||
$timestamp = 1626493236;
|
||||
$random = 'QqtzdVzxavZeXag9G5mtfzbfzFMf89p6';
|
||||
$contents = "POST\n/v3/pay/transactions/h5\n1626493236\nQqtzdVzxavZeXag9G5mtfzbfzFMf89p6\n{\"out_trade_no\":1626493236,\"description\":\"yansongda 测试 - 1626493236\",\"amount\":{\"total\":1},\"scene_info\":{\"payer_client_ip\":\"127.0.0.1\",\"h5_info\":{\"type\":\"Wap\"}},\"appid\":\"wx55955316af4ef13\",\"mchid\":\"1600314069\",\"notify_url\":\"http:\/\/127.0.0.1:8000\/wechat\/notify\"}\n";
|
||||
|
||||
$config = Pay::get(ConfigInterface::class);
|
||||
$config->set('wechat.default.mch_public_cert_path', null);
|
||||
|
||||
self::expectException(InvalidConfigException::class);
|
||||
self::expectExceptionCode(Exception::WECHAT_CONFIG_ERROR);
|
||||
self::expectExceptionMessage('Missing Wechat Config -- [mch_public_cert_path]');
|
||||
|
||||
$class = new ReflectionClass($this->plugin);
|
||||
$method = $class->getMethod('getWechatAuthorization');
|
||||
$method->setAccessible(true);
|
||||
$method->invokeArgs($this->plugin, [$params, $timestamp, $random, $contents]);
|
||||
}
|
||||
|
||||
public function testGetWechatAuthorizationWrongMchPublicCert()
|
||||
{
|
||||
$params = [
|
||||
'out_trade_no' => 1626493236,
|
||||
'description' => 'yansongda 测试 - 1626493236',
|
||||
'amount' => [
|
||||
'total' => 1,
|
||||
],
|
||||
'scene_info' => [
|
||||
'payer_client_ip' => '127.0.0.1',
|
||||
'h5_info' => [
|
||||
'type' => 'Wap',
|
||||
]
|
||||
]];
|
||||
$timestamp = 1626493236;
|
||||
$random = 'QqtzdVzxavZeXag9G5mtfzbfzFMf89p6';
|
||||
$contents = "POST\n/v3/pay/transactions/h5\n1626493236\nQqtzdVzxavZeXag9G5mtfzbfzFMf89p6\n{\"out_trade_no\":1626493236,\"description\":\"yansongda 测试 - 1626493236\",\"amount\":{\"total\":1},\"scene_info\":{\"payer_client_ip\":\"127.0.0.1\",\"h5_info\":{\"type\":\"Wap\"}},\"appid\":\"wx55955316af4ef13\",\"mchid\":\"1600314069\",\"notify_url\":\"http:\/\/127.0.0.1:8000\/wechat\/notify\"}\n";
|
||||
|
||||
$config = Pay::get(ConfigInterface::class);
|
||||
$config->set('wechat.default.mch_public_cert_path', __DIR__.'/../../Cert/foo');
|
||||
|
||||
self::expectException(InvalidConfigException::class);
|
||||
self::expectExceptionCode(Exception::WECHAT_CONFIG_ERROR);
|
||||
self::expectExceptionMessage('Parse [mch_public_cert_path] Serial Number Error');
|
||||
|
||||
$class = new ReflectionClass($this->plugin);
|
||||
$method = $class->getMethod('getWechatAuthorization');
|
||||
$method->setAccessible(true);
|
||||
$method->invokeArgs($this->plugin, [$params, $timestamp, $random, $contents]);
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ use Yansongda\Pay\Contract\HttpClientInterface;
|
||||
use Yansongda\Pay\Contract\PluginInterface;
|
||||
use Yansongda\Pay\Exception\Exception;
|
||||
use Yansongda\Pay\Exception\InvalidConfigException;
|
||||
use Yansongda\Pay\Parser\ArrayParser;
|
||||
use Yansongda\Pay\Parser\NoHttpRequestParser;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Provider\AbstractProvider;
|
||||
@ -105,6 +106,23 @@ class AbstractProviderTest extends TestCase
|
||||
$provider = new FooProviderStub();
|
||||
$provider->ignite($rocket);
|
||||
}
|
||||
|
||||
public function testArrayDirection()
|
||||
{
|
||||
$response = new Response(200, [], '{"name":"yansongda"}');
|
||||
|
||||
$http = Mockery::mock(Client::class);
|
||||
$http->shouldReceive('sendRequest')->andReturn($response);
|
||||
|
||||
Pay::set(HttpClientInterface::class, $http);
|
||||
|
||||
$plugin = [BarPlugin::class];
|
||||
|
||||
$provider = new FooProviderStub();
|
||||
$result = $provider->pay($plugin, []);
|
||||
|
||||
self::assertIsArray($result);
|
||||
}
|
||||
}
|
||||
|
||||
class FooProviderStub extends AbstractProvider
|
||||
@ -159,3 +177,19 @@ class FooPlugin implements PluginInterface
|
||||
return $next($rocket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BarPlugin implements PluginInterface
|
||||
{
|
||||
public function assembly(Rocket $rocket, Closure $next): Rocket
|
||||
{
|
||||
$rocket->setDirection(ArrayParser::class)
|
||||
->setRadar(new Request('get', ''));
|
||||
|
||||
$rocket = $next($rocket);
|
||||
|
||||
$rocket->setDestination(new Collection(['name' => 'yansongda']));
|
||||
|
||||
return $rocket;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Plugin\Alipay\LaunchPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\PreparePlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\RadarPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\RadarSignPlugin;
|
||||
use Yansongda\Pay\Plugin\Alipay\SignPlugin;
|
||||
use Yansongda\Pay\Plugin\ParserPlugin;
|
||||
use Yansongda\Pay\Tests\Stubs\Plugin\FooPluginStub;
|
||||
@ -199,7 +200,7 @@ class AlipayTest extends TestCase
|
||||
self::assertEquals(array_merge(
|
||||
[PreparePlugin::class],
|
||||
$plugins,
|
||||
[SignPlugin::class, RadarPlugin::class],
|
||||
[RadarSignPlugin::class],
|
||||
[LaunchPlugin::class, ParserPlugin::class],
|
||||
), Pay::alipay()->mergeCommonPlugins($plugins));
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Plugin\ParserPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\LaunchPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\PreparePlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\SignPlugin;
|
||||
use Yansongda\Pay\Plugin\Wechat\RadarSignPlugin;
|
||||
use Yansongda\Pay\Tests\Stubs\Plugin\FooPluginStub;
|
||||
use Yansongda\Pay\Tests\TestCase;
|
||||
|
||||
@ -42,7 +42,7 @@ class WechatTest extends TestCase
|
||||
self::assertEquals(array_merge(
|
||||
[PreparePlugin::class],
|
||||
$plugins,
|
||||
[SignPlugin::class],
|
||||
[RadarSignPlugin::class],
|
||||
[LaunchPlugin::class, ParserPlugin::class],
|
||||
), Pay::wechat()->mergeCommonPlugins($plugins));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user