first commit

This commit is contained in:
Mr.Qin
2022-08-19 19:48:37 +08:00
commit afdd648b65
3275 changed files with 631084 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
<?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\InvalidResponseException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\NoHttpRequestParser;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
use Yansongda\Supports\Str;
class CallbackPlugin implements PluginInterface
{
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
* @throws \Yansongda\Pay\Exception\InvalidResponseException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->formatPayload($rocket);
if (!($rocket->getParams()['sign'] ?? false)) {
throw new InvalidResponseException(Exception::INVALID_RESPONSE_SIGN, '', $rocket->getParams());
}
verify_alipay_sign($rocket->getParams(), $this->getSignContent($rocket->getPayload()), base64_decode($rocket->getParams()['sign']));
$rocket->setDirection(NoHttpRequestParser::class)
->setDestination($rocket->getPayload());
Logger::info('[alipay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
protected function formatPayload(Rocket $rocket): void
{
$payload = (new Collection($rocket->getParams()))->filter(function ($v, $k) {
return '' !== $v && !is_null($v) && 'sign' != $k && 'sign_type' != $k && !Str::startsWith($k, '_');
});
$rocket->setPayload($payload);
}
protected function getSignContent(Collection $payload): string
{
return urldecode($payload->sortKeys()->toString());
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Data;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class BillDownloadUrlQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.data.dataservice.bill.downloadurl.query';
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Data;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class BillEreceiptApplyPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][BillEreceiptApplyPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.data.bill.ereceipt.apply',
'biz_content' => array_merge(
[
'type' => 'FUND_DETAIL',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][BillEreceiptApplyPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Data;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class BillEreceiptQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.data.bill.ereceipt.query';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Ebpp;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class PdeductBillStatusPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.ebpp.pdeduct.bill.pay.status';
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Ebpp;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class PdeductPayPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PdeductPayPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.ebpp.pdeduct.pay',
'biz_content' => array_merge(
[
'agent_channel' => 'PUBLICFORM',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][PdeductPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Ebpp;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class PdeductSignAddPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PdeductSignAddPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.ebpp.pdeduct.sign.add',
'biz_content' => array_merge(
[
'charge_inst' => 'CQCENTERELECTRIC',
'agent_channel' => 'PUBLICPLATFORM',
'deduct_prod_code' => 'INST_DIRECT_DEDUCT',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][PdeductSignAddPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Ebpp;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class PdeductSignCancelPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PdeductSignCancelPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.ebpp.pdeduct.sign.cancel',
'biz_content' => array_merge(
[
'agent_channel' => 'PUBLICPLATFORM',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][PdeductSignCancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class AccountQueryPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][AccountQueryPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.fund.account.query',
'biz_content' => array_merge(
[
'product_code' => 'TRANS_ACCOUNT_NO_PWD',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][AccountQueryPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class AuthOrderFreezePlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][AuthOrderFreezePlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.fund.auth.order.freeze',
'biz_content' => array_merge(
[
'product_code' => 'PRE_AUTH',
],
$rocket->getParams()
),
]);
Logger::info('[alipay][AuthOrderFreezePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class AuthOrderUnfreezePlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.fund.auth.order.unfreeze';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class TransCommonQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.fund.trans.common.query';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class TransOrderQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.fund.trans.order.query';
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\ResponseParser;
use Yansongda\Pay\Rocket;
class TransPagePayPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][TransPagePayPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->setDirection(ResponseParser::class)
->mergePayload([
'method' => 'alipay.fund.trans.page.pay',
'biz_content' => $rocket->getParams(),
]);
Logger::info('[alipay][TransPagePayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class TransTobankTransferPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.fund.trans.tobank.transfer';
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Fund;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class TransUniTransferPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][TransUniTransferPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.fund.trans.uni.transfer',
'biz_content' => array_merge(
[
'biz_scene' => 'DIRECT_TRANSFER',
'product_code' => 'TRANS_ACCOUNT_NO_PWD',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][TransUniTransferPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,35 @@
<?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;
abstract class GeneralPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][GeneralPlugin] 通用插件开始装载', ['rocket' => $rocket]);
$this->doSomethingBefore($rocket);
$rocket->mergePayload([
'method' => $this->getMethod(),
'biz_content' => $rocket->getParams(),
]);
Logger::info('[alipay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
protected function doSomethingBefore(Rocket $rocket): void
{
}
abstract protected function getMethod(): string;
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay;
use Closure;
use GuzzleHttp\Psr7\Response;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
class HtmlResponsePlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
/* @var Rocket $rocket */
$rocket = $next($rocket);
Logger::info('[alipay][HtmlResponsePlugin] 插件开始装载', ['rocket' => $rocket]);
$radar = $rocket->getRadar();
$response = 'GET' === $radar->getMethod() ?
$this->buildRedirect($radar->getUri()->__toString(), $rocket->getPayload()) :
$this->buildHtml($radar->getUri()->__toString(), $rocket->getPayload());
$rocket->setDestination($response);
Logger::info('[alipay][HtmlResponsePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $rocket;
}
protected function buildRedirect(string $endpoint, Collection $payload): Response
{
$url = $endpoint.(false === strpos($endpoint, '?') ? '?' : '&').$payload->query();
$content = sprintf('<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="refresh" content="0;url=\'%1$s\'" />
<title>Redirecting to %1$s</title>
</head>
<body>
Redirecting to %1$s.
</body>
</html>', htmlspecialchars($url, ENT_QUOTES)
);
return new Response(302, ['Location' => $url], $content);
}
protected function buildHtml(string $endpoint, Collection $payload): Response
{
$sHtml = "<form id='alipay_submit' name='alipay_submit' action='".$endpoint."' method='POST'>";
foreach ($payload->all() as $key => $val) {
$val = str_replace("'", '&apos;', $val);
$sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
}
$sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
$sHtml .= "<script>document.forms['alipay_submit'].submit();</script>";
return new Response(200, [], $sHtml);
}
}

View File

@@ -0,0 +1,72 @@
<?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\InvalidResponseException;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Collection;
class LaunchPlugin implements PluginInterface
{
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
* @throws \Yansongda\Pay\Exception\InvalidResponseException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
/* @var Rocket $rocket */
$rocket = $next($rocket);
Logger::info('[alipay][LaunchPlugin] 插件开始装载', ['rocket' => $rocket]);
if (should_do_http_request($rocket->getDirection())) {
$this->verifySign($rocket);
$rocket->setDestination(
Collection::wrap(
$rocket->getDestination()->get($this->getResultKey($rocket->getPayload()))
)
);
}
Logger::info('[alipay][LaunchPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $rocket;
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
* @throws \Yansongda\Pay\Exception\InvalidResponseException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function verifySign(Rocket $rocket): 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', $response);
}
verify_alipay_sign($rocket->getParams(), json_encode($result, JSON_UNESCAPED_UNICODE), base64_decode($sign));
}
protected function getResultKey(Collection $payload): string
{
$method = $payload->get('method');
return str_replace('.', '_', $method).'_response';
}
}

View File

@@ -0,0 +1,187 @@
<?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 Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class PreparePlugin implements PluginInterface
{
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PreparePlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload($this->getPayload($rocket->getParams()));
Logger::info('[alipay][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
*/
protected function getPayload(array $params): array
{
return [
'app_id' => get_alipay_config($params)->get('app_id', ''),
'method' => '',
'format' => 'JSON',
'return_url' => $this->getReturnUrl($params),
'charset' => 'utf-8',
'sign_type' => 'RSA2',
'sign' => '',
'timestamp' => date('Y-m-d H:i:s'),
'version' => '1.0',
'notify_url' => $this->getNotifyUrl($params),
'app_auth_token' => '',
'app_cert_sn' => $this->getAppCertSn($params),
'alipay_root_cert_sn' => $this->getAlipayRootCertSn($params),
'biz_content' => [],
];
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function getReturnUrl(array $params): string
{
if (!empty($params['_return_url'])) {
return $params['_return_url'];
}
return get_alipay_config($params)->get('return_url', '');
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function getNotifyUrl(array $params): string
{
if (!empty($params['_notify_url'])) {
return $params['_notify_url'];
}
return get_alipay_config($params)->get('notify_url', '');
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
*/
protected function getAppCertSn(array $params): string
{
$path = get_alipay_config($params)->get('app_public_cert_path');
if (is_null($path)) {
throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_public_cert_path]');
}
$cert = file_get_contents($path);
$ssl = openssl_x509_parse($cert);
return $this->getCertSn($ssl['issuer'], $ssl['serialNumber']);
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function getAlipayRootCertSn(array $params): string
{
$path = get_alipay_config($params)->get('alipay_root_cert_path');
if (is_null($path)) {
throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [alipay_root_cert_path]');
}
$sn = '';
$exploded = explode('-----END CERTIFICATE-----', file_get_contents($path));
foreach ($exploded as $cert) {
if (empty(trim($cert))) {
continue;
}
$ssl = openssl_x509_parse($cert.'-----END CERTIFICATE-----');
if (false === $ssl) {
throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Invalid alipay_root_cert');
}
$detail = $this->formatCert($ssl);
if ('sha1WithRSAEncryption' == $detail['signatureTypeLN'] || 'sha256WithRSAEncryption' == $detail['signatureTypeLN']) {
$sn .= $this->getCertSn($detail['issuer'], $detail['serialNumber']).'_';
}
}
return substr($sn, 0, -1);
}
protected function getCertSn(array $issuer, string $serialNumber): string
{
return md5(
$this->array2string(array_reverse($issuer)).$serialNumber
);
}
protected function array2string(array $array): string
{
$string = [];
foreach ($array as $key => $value) {
$string[] = $key.'='.$value;
}
return implode(',', $string);
}
protected function formatCert(array $ssl): array
{
if (0 === strpos($ssl['serialNumber'], '0x')) {
$ssl['serialNumber'] = $this->hex2dec($ssl['serialNumberHex']);
}
return $ssl;
}
protected function hex2dec(string $hex): string
{
$dec = '0';
$len = strlen($hex);
for ($i = 1; $i <= $len; ++$i) {
$dec = bcadd(
$dec,
bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i), 0), 0),
0
);
}
return $dec;
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay;
use Closure;
use Psr\Http\Message\RequestInterface;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Provider\Alipay;
use Yansongda\Pay\Request;
use Yansongda\Pay\Rocket;
class RadarPlugin implements PluginInterface
{
/**
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][RadarPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->setRadar($this->getRequest($rocket));
Logger::info('[alipay][RadarPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function getRequest(Rocket $rocket): RequestInterface
{
return new Request(
$this->getMethod($rocket),
$this->getUrl($rocket),
$this->getHeaders(),
$this->getBody($rocket),
);
}
protected function getMethod(Rocket $rocket): string
{
return strtoupper($rocket->getParams()['_method'] ?? 'POST');
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function getUrl(Rocket $rocket): string
{
$config = get_alipay_config($rocket->getParams());
return Alipay::URL[$config->get('mode', Pay::MODE_NORMAL)];
}
protected function getHeaders(): array
{
return [
'Content-Type' => 'application/x-www-form-urlencoded',
];
}
protected function getBody(Rocket $rocket): string
{
return $rocket->getPayload()->query();
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Closure;
use GuzzleHttp\Psr7\Response;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\AppPayPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Arr;
use Yansongda\Supports\Collection;
class AppShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
AppPayPlugin::class,
$this->buildResponse(),
];
}
protected function buildResponse(): PluginInterface
{
return new class() implements PluginInterface {
public function assembly(Rocket $rocket, Closure $next): Rocket
{
$rocket->setDestination(new Response());
/* @var Rocket $rocket */
$rocket = $next($rocket);
$response = $this->buildHtml($rocket->getPayload());
return $rocket->setDestination($response);
}
protected function buildHtml(Collection $payload): Response
{
return new Response(200, [], Arr::query($payload->all()));
}
};
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\CancelPlugin;
class CancelShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
CancelPlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\ClosePlugin;
class CloseShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
ClosePlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\CreatePlugin;
class MiniShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
CreatePlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\PayPlugin;
class PosShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
PayPlugin::class,
];
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Plugin\Alipay\Fund\TransOrderQueryPlugin;
use Yansongda\Pay\Plugin\Alipay\Trade\FastRefundQueryPlugin;
use Yansongda\Pay\Plugin\Alipay\Trade\QueryPlugin;
class QueryShortcut implements ShortcutInterface
{
/**
* @throws \Yansongda\Pay\Exception\InvalidParamsException
*/
public function getPlugins(array $params): array
{
$typeMethod = ($params['_type'] ?? 'default').'Plugins';
if (isset($params['out_request_no'])) {
return $this->refundPlugins();
}
if (method_exists($this, $typeMethod)) {
return $this->{$typeMethod}();
}
throw new InvalidParamsException(Exception::SHORTCUT_QUERY_TYPE_ERROR, "Query type [$typeMethod] not supported");
}
protected function defaultPlugins(): array
{
return [
QueryPlugin::class,
];
}
protected function refundPlugins(): array
{
return [
FastRefundQueryPlugin::class,
];
}
protected function transferPlugins(): array
{
return [
TransOrderQueryPlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\RefundPlugin;
class RefundShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
RefundPlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Trade\PreCreatePlugin;
class ScanShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
PreCreatePlugin::class,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\Fund\TransUniTransferPlugin;
class TransferShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
TransUniTransferPlugin::class,
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\HtmlResponsePlugin;
use Yansongda\Pay\Plugin\Alipay\Trade\WapPayPlugin;
class WapShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
WapPayPlugin::class,
HtmlResponsePlugin::class,
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Shortcut;
use Yansongda\Pay\Contract\ShortcutInterface;
use Yansongda\Pay\Plugin\Alipay\HtmlResponsePlugin;
use Yansongda\Pay\Plugin\Alipay\Trade\PagePayPlugin;
class WebShortcut implements ShortcutInterface
{
public function getPlugins(array $params): array
{
return [
PagePayPlugin::class,
HtmlResponsePlugin::class,
];
}
}

View File

@@ -0,0 +1,92 @@
<?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 Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Supports\Str;
class SignPlugin implements PluginInterface
{
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @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][SignPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->formatPayload($rocket);
$sign = $this->getSign($rocket);
$rocket->mergePayload(['sign' => $sign]);
Logger::info('[alipay][SignPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
protected function formatPayload(Rocket $rocket): void
{
$payload = $rocket->getPayload()->filter(function ($v, $k) {
return '' !== $v && !is_null($v) && 'sign' != $k;
});
$contents = array_filter($payload->get('biz_content', []), function ($v, $k) {
return !Str::startsWith(strval($k), '_');
}, ARRAY_FILTER_USE_BOTH);
$rocket->setPayload(
$payload->merge(['biz_content' => json_encode($contents)])
);
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @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);
$sign = base64_encode($sign);
!is_resource($privateKey) ?: openssl_free_key($privateKey);
return $sign;
}
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\InvalidConfigException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*
* @return resource|string
*/
protected function getPrivateKey(array $params)
{
$privateKey = get_alipay_config($params)->get('app_secret_cert');
if (is_null($privateKey)) {
throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_secret_cert]');
}
return get_public_or_private_cert($privateKey);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Tools;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class OpenAuthTokenAppPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.open.auth.token.app';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Tools;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class OpenAuthTokenAppQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.open.auth.token.app.query';
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Tools;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
use Yansongda\Pay\Rocket;
class SystemOauthTokenPlugin extends GeneralPlugin
{
protected function doSomethingBefore(Rocket $rocket): void
{
$rocket->mergePayload($rocket->getParams());
}
protected function getMethod(): string
{
return 'alipay.system.oauth.token';
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\ResponseParser;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\SupportServiceProviderTrait;
class AppPayPlugin implements PluginInterface
{
use SupportServiceProviderTrait;
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][AppPayPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->loadAlipayServiceProvider($rocket);
$rocket->setDirection(ResponseParser::class)
->mergePayload([
'method' => 'alipay.trade.app.pay',
'biz_content' => array_merge(
['product_code' => 'QUICK_MSECURITY_PAY'],
$rocket->getParams(),
),
]);
Logger::info('[alipay][AppPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class CancelPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.cancel';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class ClosePlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.close';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class CreatePlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.create';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class FastRefundQueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.fastpay.refund.query';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class OrderPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.order.pay';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class OrderSettlePlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.order.settle';
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\ResponseParser;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\SupportServiceProviderTrait;
class PagePayPlugin implements PluginInterface
{
use SupportServiceProviderTrait;
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PagePayPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->loadAlipayServiceProvider($rocket);
$rocket->setDirection(ResponseParser::class)
->mergePayload([
'method' => 'alipay.trade.page.pay',
'biz_content' => array_merge(
['product_code' => 'FAST_INSTANT_TRADE_PAY'],
$rocket->getParams()
),
]);
Logger::info('[alipay][PagePayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\ResponseParser;
use Yansongda\Pay\Rocket;
class PageRefundPlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PageRefundPlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->setDirection(ResponseParser::class)
->mergePayload([
'method' => 'alipay.trade.page.refund',
'biz_content' => $rocket->getParams(),
]);
Logger::info('[alipay][PageRefundPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\SupportServiceProviderTrait;
class PayPlugin implements PluginInterface
{
use SupportServiceProviderTrait;
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][PayPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->loadAlipayServiceProvider($rocket);
$rocket->mergePayload([
'method' => 'alipay.trade.pay',
'biz_content' => array_merge(
[
'product_code' => 'FACE_TO_FACE_PAYMENT',
'scene' => 'bar_code',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\SupportServiceProviderTrait;
class PreCreatePlugin extends GeneralPlugin
{
use SupportServiceProviderTrait;
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
protected function doSomethingBefore(Rocket $rocket): void
{
$this->loadAlipayServiceProvider($rocket);
}
protected function getMethod(): string
{
return 'alipay.trade.precreate';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class QueryPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.query';
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Yansongda\Pay\Plugin\Alipay\GeneralPlugin;
class RefundPlugin extends GeneralPlugin
{
protected function getMethod(): string
{
return 'alipay.trade.refund';
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\Trade;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Parser\ResponseParser;
use Yansongda\Pay\Rocket;
use Yansongda\Pay\Traits\SupportServiceProviderTrait;
class WapPayPlugin implements PluginInterface
{
use SupportServiceProviderTrait;
/**
* @throws \Yansongda\Pay\Exception\ContainerDependencyException
* @throws \Yansongda\Pay\Exception\ContainerException
* @throws \Yansongda\Pay\Exception\ServiceNotFoundException
*/
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][WapPayPlugin] 插件开始装载', ['rocket' => $rocket]);
$this->loadAlipayServiceProvider($rocket);
$rocket->setDirection(ResponseParser::class)
->mergePayload([
'method' => 'alipay.trade.wap.pay',
'biz_content' => array_merge(
[
'product_code' => 'QUICK_WAP_PAY',
],
$rocket->getParams(),
),
]);
Logger::info('[alipay][WapPayPlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Yansongda\Pay\Plugin\Alipay\User;
use Closure;
use Yansongda\Pay\Contract\PluginInterface;
use Yansongda\Pay\Logger;
use Yansongda\Pay\Rocket;
class InfoSharePlugin implements PluginInterface
{
public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::info('[alipay][InfoSharePlugin] 插件开始装载', ['rocket' => $rocket]);
$rocket->mergePayload([
'method' => 'alipay.user.info.share',
'auth_token' => $rocket->getParams()['auth_token'] ?? '',
]);
Logger::info('[alipay][InfoSharePlugin] 插件装载完毕', ['rocket' => $rocket]);
return $next($rocket);
}
}