refactor: 优化支付宝 launch 插件代码 (#678)

* deprecated: 支付宝/微信 `RadarPlugin`, `SignPlugin` 已废弃,使用 `RadarSignPlugin` 代替
This commit is contained in:
yansongda 2022-09-12 23:11:53 +08:00 committed by GitHub
parent ebc939e1d7
commit 9412db512f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 599 additions and 28 deletions

View File

@ -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

View File

@ -1,5 +1,7 @@
parameters:
reportUnmatchedIgnoredErrors: false
excludePaths:
- src/Service/ContainerServiceProvider.php
ignoreErrors:
- '#.* Illuminate\\Container\\Container.*#'
- '#.* Hyperf\\Utils\\ApplicationContext.*#'
- '#.* think\\Container.*#'

View File

@ -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);

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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
{
/**

View 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();
}
}

View File

@ -16,6 +16,9 @@ use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Str;
/**
* @deprecated use RadarSignPlugin instead
*/
class SignPlugin implements PluginInterface
{
/**

View 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();
}
}

View File

@ -20,6 +20,9 @@ use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use Yansongda\Supports\Str;
/**
* @deprecated use RadarSignPlugin instead
*/
class SignPlugin implements PluginInterface
{
/**

View File

@ -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;
}
/**

View File

@ -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],
);
}

View File

@ -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],
);
}

View File

@ -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 = [

View File

@ -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

View 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());
}
}

View 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]);
}
}

View File

@ -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;
}
}

View File

@ -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));
}

View File

@ -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));
}