diff --git a/src/Event/RequestReceived.php b/src/Event/RequestReceived.php index 2f05609..5733c94 100755 --- a/src/Event/RequestReceived.php +++ b/src/Event/RequestReceived.php @@ -8,6 +8,11 @@ use Yansongda\Pay\Rocket; class RequestReceived extends Event { + /** + * @var string + */ + public $provider; + /** * @var array|\Psr\Http\Message\ServerRequestInterface|null */ @@ -25,8 +30,9 @@ class RequestReceived extends Event * * @param array|\Psr\Http\Message\ServerRequestInterface|null $contents */ - public function __construct($contents, ?array $params, ?Rocket $rocket) + public function __construct(string $provider, $contents, ?array $params, ?Rocket $rocket) { + $this->provider = $provider; $this->contents = $contents; $this->params = $params; diff --git a/src/Plugin/Wechat/CallbackPlugin.php b/src/Plugin/Wechat/CallbackPlugin.php new file mode 100644 index 0000000..d41651b --- /dev/null +++ b/src/Plugin/Wechat/CallbackPlugin.php @@ -0,0 +1,63 @@ + $rocket]); + + $this->filterPayload($rocket); + + if (!($rocket->getParams()['sign'] ?? false)) { + throw new InvalidResponseException(InvalidResponseException::INVALID_RESPONSE_SIGN, '', $rocket->getParams()); + } + + if (!verify_alipay_response( + $rocket->getParams(), + $this->getSignContent($rocket->getPayload()), + base64_decode($rocket->getParams()['sign'])) + ) { + throw new InvalidResponseException(InvalidResponseException::INVALID_RESPONSE_SIGN, '', $rocket->getParams()); + } + + $rocket->setDirection(NoHttpRequestParser::class) + ->setDestination($rocket->getPayload()); + + Logger::info('[alipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + protected function filterPayload(Rocket $rocket): void + { + $payload = (new Collection($rocket->getParams()))->filter(function ($v, $k) { + return '' !== $v && !is_null($v) && 'sign' != $k && 'sign_type' != $k; + }); + + $rocket->setPayload($payload); + } + + protected function getSignContent(Collection $payload): string + { + return urldecode($payload->sortKeys()->toString()); + } +} diff --git a/src/Plugin/Wechat/SignPlugin.php b/src/Plugin/Wechat/SignPlugin.php index b566b91..07a21e4 100644 --- a/src/Plugin/Wechat/SignPlugin.php +++ b/src/Plugin/Wechat/SignPlugin.php @@ -55,7 +55,7 @@ class SignPlugin implements PluginInterface $config->get('mch_id', ''), $random, $timestamp, - $config->get('mch_public_cert_serial_number', ''), + $this->getMchPublicCertSerialNumber($config->get('mch_public_cert_path')), $this->getSign($rocket, $timestamp, $random) ); @@ -114,4 +114,23 @@ class SignPlugin implements PluginInterface return get_public_crt_or_private_cert($privateKey); } + + /** + * @throws \Yansongda\Pay\Exception\InvalidConfigException + */ + protected function getMchPublicCertSerialNumber(?string $path): string + { + if (is_null($path)) { + throw new InvalidConfigException(InvalidConfigException::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_public_cert_path]'); + } + + $cert = file_get_contents($path); + $ssl = openssl_x509_parse($cert); + + if (empty($ssl['serialNumberHex'])) { + throw new InvalidConfigException(InvalidConfigException::WECHAT_CONFIG_ERROR, 'Parse [mch_public_cert_path] Serial Number Error'); + } + + return $ssl['serialNumberHex']; + } } diff --git a/src/Provider/AbstractProvider.php b/src/Provider/AbstractProvider.php index 274b5d0..860b6a6 100644 --- a/src/Provider/AbstractProvider.php +++ b/src/Provider/AbstractProvider.php @@ -56,7 +56,7 @@ abstract class AbstractProvider implements ProviderInterface */ public function pay(array $plugins, array $params) { - Logger::info('[AbstractProvider] 即将进行支付操作', func_get_args()); + Logger::info('[AbstractProvider] 即将进行 pay 操作', func_get_args()); Event::dispatch(new Event\PayStarted($plugins, $params, null)); diff --git a/src/Provider/Alipay.php b/src/Provider/Alipay.php index e3842ec..7065839 100644 --- a/src/Provider/Alipay.php +++ b/src/Provider/Alipay.php @@ -117,7 +117,7 @@ class Alipay extends AbstractProvider */ public function verify($contents = null, ?array $params = null): Collection { - Event::dispatch(new Event\RequestReceived($contents, $params, null)); + Event::dispatch(new Event\RequestReceived('alipay', $contents, $params, null)); $response = $this->getCallbackParams($contents); diff --git a/src/Provider/Wechat.php b/src/Provider/Wechat.php index 36ac53b..2a9ea0e 100644 --- a/src/Provider/Wechat.php +++ b/src/Provider/Wechat.php @@ -5,12 +5,14 @@ declare(strict_types=1); namespace Yansongda\Pay\Provider; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Yansongda\Pay\Event; use Yansongda\Pay\Exception\InvalidParamsException; use Yansongda\Pay\Pay; 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; @@ -103,6 +105,13 @@ class Wechat extends AbstractProvider */ public function verify($contents = null, ?array $params = null): Collection { + Event::dispatch(new Event\RequestReceived('wechat', $contents, $params, null)); + + $response = $this->getCallbackParams($contents); + + return $this->pay( + [CallbackPlugin::class], $response->merge($params)->all() + ); } public function success(): ResponseInterface @@ -123,4 +132,25 @@ class Wechat extends AbstractProvider [LaunchPlugin::class, ParserPlugin::class], ); } + + /** + * @param array|ServerRequestInterface|null $contents + */ + protected function getCallbackParams($contents = null): Collection + { + if (is_array($contents)) { + return Collection::wrap($contents); + } + + if ($contents instanceof ServerRequestInterface) { + return Collection::wrap('GET' === $contents->getMethod() ? $contents->getQueryParams() : + $contents->getParsedBody()); + } + + $request = ServerRequest::fromGlobals(); + + return Collection::wrap( + array_merge($request->getQueryParams(), $request->getParsedBody()) + ); + } }