From 20be6858cc3fe61a30af6ce92b7b67e586f41032 Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 10:28:12 +0800 Subject: [PATCH 01/17] =?UTF-8?q?feat:=20epay=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 76 +++++++++++++ src/Exception/Exception.php | 2 + src/Functions.php | 12 ++ src/Pay.php | 4 + src/Plugin/Epay/AddPayloadSignPlugin.php | 68 +++++++++++ src/Plugin/Epay/AddRadarPlugin.php | 64 +++++++++++ src/Plugin/Epay/CallbackPlugin.php | 78 +++++++++++++ src/Plugin/Epay/GeneralPlugin.php | 33 ++++++ src/Plugin/Epay/Pay/Scan/CancelPlugin.php | 15 +++ src/Plugin/Epay/Pay/Scan/PrepayPlugin.php | 44 ++++++++ src/Plugin/Epay/Pay/Scan/QueryPlugin.php | 23 ++++ src/Plugin/Epay/Pay/Scan/RefundPlugin.php | 23 ++++ src/Plugin/Epay/ResponsePlugin.php | 51 +++++++++ src/Plugin/Epay/StartPlugin.php | 51 +++++++++ src/Plugin/Epay/VerifySignaturePlugin.php | 102 +++++++++++++++++ src/Provider/Epay.php | 132 ++++++++++++++++++++++ src/Service/EpayServiceProvider.php | 24 ++++ src/Shortcut/Epay/CancelShortcut.php | 30 +++++ src/Shortcut/Epay/QueryShortcut.php | 30 +++++ src/Shortcut/Epay/RefundShortcut.php | 34 ++++++ src/Shortcut/Epay/ScanShortcut.php | 30 +++++ tests/Cert/EpayCert.cer | 15 +++ tests/Cert/EpayKey.pem | 16 +++ tests/Cert/jschina.cer | 14 +++ tests/PayTest.php | 3 + tests/Provider/EpayTest.php | 61 ++++++++++ tests/TestCase.php | 36 +++++- 27 files changed, 1066 insertions(+), 5 deletions(-) create mode 100644 src/Plugin/Epay/AddPayloadSignPlugin.php create mode 100644 src/Plugin/Epay/AddRadarPlugin.php create mode 100644 src/Plugin/Epay/CallbackPlugin.php create mode 100644 src/Plugin/Epay/GeneralPlugin.php create mode 100644 src/Plugin/Epay/Pay/Scan/CancelPlugin.php create mode 100644 src/Plugin/Epay/Pay/Scan/PrepayPlugin.php create mode 100644 src/Plugin/Epay/Pay/Scan/QueryPlugin.php create mode 100644 src/Plugin/Epay/Pay/Scan/RefundPlugin.php create mode 100644 src/Plugin/Epay/ResponsePlugin.php create mode 100644 src/Plugin/Epay/StartPlugin.php create mode 100644 src/Plugin/Epay/VerifySignaturePlugin.php create mode 100644 src/Provider/Epay.php create mode 100644 src/Service/EpayServiceProvider.php create mode 100644 src/Shortcut/Epay/CancelShortcut.php create mode 100644 src/Shortcut/Epay/QueryShortcut.php create mode 100644 src/Shortcut/Epay/RefundShortcut.php create mode 100644 src/Shortcut/Epay/ScanShortcut.php create mode 100644 tests/Cert/EpayCert.cer create mode 100644 tests/Cert/EpayKey.pem create mode 100644 tests/Cert/jschina.cer create mode 100644 tests/Provider/EpayTest.php diff --git a/README.md b/README.md index 3b6d1b5f3..13b501a13 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,11 @@ yansongda/pay 100% 兼容 支付宝/微信/银联 所有功能(包括服务商 - 刷卡支付 - 扫码支付 - ... +- +### e融支付(江苏银行) + +- 聚合扫码支付(微信,支付宝,银联,e融) +- ... ## 安装 ```shell @@ -279,6 +284,77 @@ class WechatController } ``` +### e融支付(epay) +```php + [ + 'default' => [ + // 服务代码 + 'svr_code' => '', + // 必填-合作商ID + 'partner_id' => '', + // 必填-公私钥对编号 + 'public_key_code' => '00', + // 必填-商户私钥(加密签名) + 'mch_secret_cert_path' => '', + // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) + 'mch_public_cert_path' => '', + // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) + 'epay_public_cert_path' => '', + //支付通知地址 + 'notify_url' => '', + // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:测试环境 + 'mode' => Pay::MODE_NORMAL, + ] + ], + 'logger' => [ // optional + 'enable' => false, + 'file' => './logs/epay.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + ]; + + public function index() + { + $order = [ + 'outTradeNo' => time().'', + 'proInfo' => 'subject-测试', + 'totalFee'=> 1, + ]; + + $pay = Pay::epay($this->config)->scan($order); + } + + public function notifyCallback() + { + $pay = Pay::epay($this->config); + + try{ + $data = $pay->callback(); // 是的,验签就这么简单! + } catch (\Exception $e) { + // $e->getMessage(); + } + + return $pay->success(); + } +} +``` + ## 代码贡献 由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 73801209e..9c400a44d 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -55,6 +55,8 @@ class Exception extends \Exception public const CONFIG_UNIPAY_INVALID = 9403; + public const CONFIG_EPAY_INVALID = 9404; + /** * 关于签名. */ diff --git a/src/Functions.php b/src/Functions.php index 5f7c30292..14b9ff461 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -23,6 +23,7 @@ use Yansongda\Pay\Plugin\Wechat\V3\AddPayloadSignaturePlugin; use Yansongda\Pay\Plugin\Wechat\V3\WechatPublicCertsPlugin; use Yansongda\Pay\Provider\Alipay; +use Yansongda\Pay\Provider\Epay; use Yansongda\Pay\Provider\Unipay; use Yansongda\Pay\Provider\Wechat; use Yansongda\Supports\Collection; @@ -591,3 +592,14 @@ function verify_unipay_sign_qra(array $config, array $destination): void throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证银联签名失败', $destination); } } + +function get_epay_url(array $config, ?Collection $payload): string +{ + $url = get_radar_url($config, $payload) ?? ''; + + if (str_starts_with($url, 'http')) { + return $url; + } + + return Epay::URL[$config['mode'] ?? Pay::MODE_NORMAL]; +} diff --git a/src/Pay.php b/src/Pay.php index a6d1dd8e9..87328e6e0 100644 --- a/src/Pay.php +++ b/src/Pay.php @@ -10,9 +10,11 @@ use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Pay\Provider\Alipay; +use Yansongda\Pay\Provider\Epay; use Yansongda\Pay\Provider\Unipay; use Yansongda\Pay\Provider\Wechat; use Yansongda\Pay\Service\AlipayServiceProvider; +use Yansongda\Pay\Service\EpayServiceProvider; use Yansongda\Pay\Service\UnipayServiceProvider; use Yansongda\Pay\Service\WechatServiceProvider; @@ -20,6 +22,7 @@ * @method static Alipay alipay(array $config = [], $container = null) * @method static Wechat wechat(array $config = [], $container = null) * @method static Unipay unipay(array $config = [], $container = null) + * @method static Epay epay(array $config = [], $container = null) */ class Pay { @@ -42,6 +45,7 @@ class Pay AlipayServiceProvider::class, WechatServiceProvider::class, UnipayServiceProvider::class, + EpayServiceProvider::class, ]; /** diff --git a/src/Plugin/Epay/AddPayloadSignPlugin.php b/src/Plugin/Epay/AddPayloadSignPlugin.php new file mode 100644 index 000000000..1ac5e64f8 --- /dev/null +++ b/src/Plugin/Epay/AddPayloadSignPlugin.php @@ -0,0 +1,68 @@ + $rocket]); + + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); + $payload = $rocket->getPayload(); + + if (empty($payload) || $payload->isEmpty()) { + throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: epay支付必要参数缺失。可能插件用错顺序,应该先使用 `业务插件`'); + } + + $pkey = $this->getPrivateKey($config); + $sign = $this->getSignature($pkey, $payload); + $rocket->mergePayload([ + 'signType' => 'RSA', + 'sign' => $sign, + ]); + + Logger::info('[epay][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + protected function getSignature(string $pkey, Collection $payload): string + { + $content = $payload->sortKeys()->toString(); + + openssl_sign($content, $signature, $pkey); + + return base64_encode($signature); + } + + protected function getPrivateKey(array $config): string + { + $privateCertPath = $config['mch_secret_cert_path'] ?? ''; + + if (!$privateCertPath) { + throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); + } + + return file_get_contents($privateCertPath); + } +} diff --git a/src/Plugin/Epay/AddRadarPlugin.php b/src/Plugin/Epay/AddRadarPlugin.php new file mode 100644 index 000000000..bb23e4e9f --- /dev/null +++ b/src/Plugin/Epay/AddRadarPlugin.php @@ -0,0 +1,64 @@ + $rocket]); + + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); + $payload = $rocket->getPayload(); + + $rocket->setRadar(new Request( + strtoupper($params['_method'] ?? 'POST'), + get_epay_url($config, $payload), + $this->getHeaders(), + $this->getBody($payload), + )); + + Logger::info('[epay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + protected function getMethod(array $params): string + { + return strtoupper($params['_method'] ?? 'POST'); + } + + protected function getHeaders(): array + { + return [ + 'Content-Type' => 'text/html', + 'User-Agent' => 'yansongda/pay-v3', + ]; + } + + protected function getBody(Collection $payload): string + { + $sign = $payload->get('sign'); + $signType = $payload->get('signType'); + $payload->forget('sign'); + $payload->forget('signType'); + $payload = $payload->sortKeys(); + $payload->set('sign', $sign); + $payload->set('signType', $signType); + + return $payload->toString(); + } +} diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php new file mode 100644 index 000000000..8b417b0d7 --- /dev/null +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -0,0 +1,78 @@ + $rocket]); + + $this->formatRequestAndParams($rocket); + + $params = $rocket->getParams(); + $config = get_provider_config('alipay', $params); + + $payload = $rocket->getPayload(); + $signature = $payload->get('sign'); + + $payload->forget('sign'); + $payload->forget('signType'); + + $this->verifySign($config, $payload, $signature); + + $rocket->setDestination($rocket->getPayload()); + + Logger::info('[epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + protected function verifySign(array $config, Collection $payload, ?string $signature = null): void + { + if (!$signature) { + throw new InvalidResponseException(Exception::SIGN_ERROR, 'Verify Epay payload Sign Failed: sign is empty', $payload); + } + + $publicCert = $config['epay_public_cert_path'] ?? null; + + if (empty($publicCert)) { + throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [epay_public_cert_path]'); + } + + $result = 1 === openssl_verify( + $payload->sortKeys()->toString(), + base64_decode($signature), + file_get_contents($publicCert) + ); + if (!$result) { + throw new InvalidResponseException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); + } + } + + /** + * @throws InvalidParamsException + */ + protected function formatRequestAndParams(Rocket $rocket): void + { + $request = $rocket->getParams()['request'] ?? null; + if (!$request instanceof Collection) { + throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID); + } + $rocket->setPayload($request)->setParams($rocket->getParams()['params'] ?? []); + } +} diff --git a/src/Plugin/Epay/GeneralPlugin.php b/src/Plugin/Epay/GeneralPlugin.php new file mode 100644 index 000000000..75e7701fa --- /dev/null +++ b/src/Plugin/Epay/GeneralPlugin.php @@ -0,0 +1,33 @@ + $rocket]); + + $this->doSomethingBefore($rocket); + + $rocket->setPacker(QueryPacker::class)->mergePayload([ + 'service' => $this->getService(), + ]); + + Logger::info('[epay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + protected function doSomethingBefore(Rocket $rocket): void {} + + abstract protected function getService(): string; +} diff --git a/src/Plugin/Epay/Pay/Scan/CancelPlugin.php b/src/Plugin/Epay/Pay/Scan/CancelPlugin.php new file mode 100644 index 000000000..f84284679 --- /dev/null +++ b/src/Plugin/Epay/Pay/Scan/CancelPlugin.php @@ -0,0 +1,15 @@ +getPayload()['notify_url'])) { + return; + } + + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); + + $backUrl = $config['notify_url'] ?? null; + if (!$backUrl) { + throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [notify_url]'); + } + $rocket->mergePayload([ + 'backUrl' => $backUrl, + ]); + } +} diff --git a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php new file mode 100644 index 000000000..29daa29d5 --- /dev/null +++ b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php @@ -0,0 +1,23 @@ +mergePayload([ + 'deviceNo' => '1234567890', + ]); + } +} diff --git a/src/Plugin/Epay/Pay/Scan/RefundPlugin.php b/src/Plugin/Epay/Pay/Scan/RefundPlugin.php new file mode 100644 index 000000000..e2aab8a03 --- /dev/null +++ b/src/Plugin/Epay/Pay/Scan/RefundPlugin.php @@ -0,0 +1,23 @@ +mergePayload([ + 'deviceNo' => '1234567890', + ]); + } +} diff --git a/src/Plugin/Epay/ResponsePlugin.php b/src/Plugin/Epay/ResponsePlugin.php new file mode 100644 index 000000000..d0d1ba83e --- /dev/null +++ b/src/Plugin/Epay/ResponsePlugin.php @@ -0,0 +1,51 @@ + $rocket]); + + $this->validateResponse($rocket); + + Logger::info('[Wechat][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $rocket; + } + + /** + * @throws InvalidResponseException + */ + protected function validateResponse(Rocket $rocket): void + { + $destination = $rocket->getDestination(); + $destinationOrigin = $rocket->getDestinationOrigin(); + if ($destinationOrigin instanceof ResponseInterface + && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) { + throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, 'epay返回状态码异常,请检查参数是否错误', $rocket->getDestination()); + } + + if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) { + throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, sprintf('Epay返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); + } + } +} diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Epay/StartPlugin.php new file mode 100644 index 000000000..74cddd120 --- /dev/null +++ b/src/Plugin/Epay/StartPlugin.php @@ -0,0 +1,51 @@ + $rocket]); + + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); + + $rocket->mergePayload(array_merge( + $params, + [ + 'createData' => date('Ymd'), + 'createTime' => date('His'), + 'bizDate' => date('Ymd'), + 'msgId' => Str::uuidV4(), + 'svrCode' => $config['svr_code'] ?? '', + 'partnerId' => $config['partner_id'] ?? '', + 'channelNo' => 'm', + 'publicKeyCode' => $config['public_key_code'] ?? '', + 'version' => 'v1.0.0', + 'charset' => 'utf-8', + ] + )); + Logger::info('[epay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } +} diff --git a/src/Plugin/Epay/VerifySignaturePlugin.php b/src/Plugin/Epay/VerifySignaturePlugin.php new file mode 100644 index 000000000..e268e49fc --- /dev/null +++ b/src/Plugin/Epay/VerifySignaturePlugin.php @@ -0,0 +1,102 @@ + $rocket]); + + if (should_do_http_request($rocket->getDirection())) { + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); + $body = (string) $rocket->getDestinationOrigin()->getBody(); + $this->verifySign($config, $body); + } + + Logger::info('[Epay][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $rocket; + } + + protected function verifySign(array $config, string $body): void + { + // 解析签名 + $signatureData = $this->getSignatureData($body); + + if (!$signatureData['sign']) { + throw new InvalidResponseException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Verify Epay Response Sign Failed: sign is empty', $body); + } + + $publicCert = $config['epay_public_cert_path'] ?? null; + if (empty($publicCert)) { + throw new InvalidConfigException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Missing Epay Config -- [epay_public_cert_path]'); + } + $result = 1 === openssl_verify( + $signatureData['data'], + base64_decode($signatureData['sign']), + file_get_contents($publicCert) + ); + if (!$result) { + throw new InvalidResponseException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Verify Epay Response Sign Failed', func_get_args()); + } + } + + protected function query(string $body): array + { + $result = []; + foreach (explode('&', $body) as $item) { + $pos = strpos($item, '='); + if (!$pos) { + continue; + } + $result[substr($item, 0, $pos)] = substr($item, $pos + 1); + } + + return $result; + } + + private function getSignatureData(string $body): array + { + if (Str::contains($body, '&-&')) { + $beginIndex = strpos($body, '&signType='); + $endIndex = strpos($body, '&-&'); + $data = substr($body, 0, $beginIndex).substr($body, $endIndex); + + $signIndex = strpos($body, '&sign='); + $signature = substr($body, $signIndex + strlen('&sign='), $endIndex - ($signIndex + strlen('&sign='))); + } else { + $result = Arr::wrapQuery($body, true); + $result = Collection::wrap($result); + $signature = $result->get('sign'); + $result->forget('sign'); + $result->forget('signType'); + $data = $result->sortKeys()->toString(); + } + + return [ + 'sign' => $signature, + 'data' => $data, + ]; + } +} diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php new file mode 100644 index 000000000..795208d28 --- /dev/null +++ b/src/Provider/Epay.php @@ -0,0 +1,132 @@ + 'https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm', + Pay::MODE_SANDBOX => 'https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', + ]; + + public function __call($name, $params) + { + $plugin = '\\Yansongda\\Pay\\Shortcut\\Epay\\'.Str::studly($name).'Shortcut'; + + return Artful::shortcut($plugin, ...$params); + } + + /** + * @throws ContainerException + * @throws InvalidParamsException + */ + public function pay(array $plugins, array $params): null|Collection|MessageInterface|Rocket + { + return Artful::artful($plugins, $params); + } + + public function mergeCommonPlugins(array $plugins): array + { + return array_merge( + [StartPlugin::class], + $plugins, + [AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class], + ); + } + + public function cancel($order): Collection|Rocket + { + $order = is_array($order) ? $order : ['outTradeNo' => $order]; + + Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); + + return $this->__call('cancel', [$order]); + } + + public function close($order): Collection|Rocket + { + throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support close api'); + } + + public function refund(array $order): Collection|Rocket + { + Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); + + return $this->__call('refund', [$order]); + } + + public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket + { + $request = $this->getCallbackParams($contents); + + Event::dispatch(new CallbackReceived('epay', $request, $params, null)); + + return $this->pay( + [CallbackPlugin::class], + ['request' => $request, 'params' => $params] + ); + } + + public function success(): ResponseInterface + { + return new Response( + 200, + ['Content-Type' => 'text/html'], + 'success', + ); + } + + public function query(array $order): Collection|Rocket + { + $order = is_array($order) ? $order : ['outTradeNo' => $order]; + + Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); + + return $this->__call('query', [$order]); + } + + protected function getCallbackParams($contents = null): Collection + { + if (is_array($contents)) { + return Collection::wrap($contents); + } + + if ($contents instanceof ServerRequestInterface) { + return Collection::wrap($contents->getParsedBody()); + } + + $request = ServerRequest::fromGlobals(); + + return Collection::wrap($request->getParsedBody()); + } +} diff --git a/src/Service/EpayServiceProvider.php b/src/Service/EpayServiceProvider.php new file mode 100644 index 000000000..eecc09d2c --- /dev/null +++ b/src/Service/EpayServiceProvider.php @@ -0,0 +1,24 @@ + 'yansongda1', '_force' => true]); diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php new file mode 100644 index 000000000..36e327da1 --- /dev/null +++ b/tests/Provider/EpayTest.php @@ -0,0 +1,61 @@ +foo(); + } + + public function testShortcutIncompatible() + { + self::expectException(InvalidParamsException::class); + self::expectExceptionCode(Exception::PARAMS_SHORTCUT_INVALID); + + Pay::wechat()->foo(); + } + + public function testMergeCommonPlugins() + { + Pay::config([]); + $plugins = [FooPluginStub::class]; + + self::assertEquals(array_merge( + [StartPlugin::class], + $plugins, + [AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class], + ), Pay::epay()->mergeCommonPlugins($plugins)); + } + public function testSuccess() + { + $result = Pay::epay()->success(); + + self::assertInstanceOf(ResponseInterface::class, $result); + self::assertStringContainsString('success', (string) $result->getBody()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index cd9be3c15..911b08473 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,11 +1,18 @@ '9021000122682882', 'app_secret_cert' => 'MIIEpAIBAAKCAQEApSA9oxvcqfbgpgkxXvyCpnxaR6TPaEMh/ij+PhF8180zL82ic4whkrRlcu1Y179AKEZNar71Ugi37fKcXWLerjPOeb8WHnZgNG19gkAcOIqZPRPpJ1eRtwKEclIzt+j3H/wgXWkD7BTr61RjuAcviyvDVbAJ/TPlMqXdJFIuJwZblN2WblIv+4Dm1iPOB+fVCU3rsgg4eajf3HrZ7sq6fBhQhO5krDmIIYGsFZ+fohEgnLkBaF0gqNUb5Yb4PBfaEcu8Hcwq+XyBSMOVOIABRPQVDedW2sE/2NsLkR62DaEe/Ri9VUDJe0pE39P+X22DicJ3E3yrxvdioMnLtDqEuwIDAQABAoIBAQCSHZ1tH9J7c8IGKkxNyROzToZ0rxn5IK6LwKp5MfBO5X1N56DArldnAcpjkDL1dn7HJK6Mrr1WAfD/1ZcX680wSReEE9r2ybkHq3tMLn7KaZp/uYavEYYXc1rP7n1lV/iVjPz2q16VIU5Bx0MWLQWdGPSYdlXggHNoBe1RnobIcCGOVe9HlzCBtWzGpCZvMlqRbCuWAdp14aCkaJqpRxG4PY9Kd/NzELvhnCd9k8e7G2qcwx6gAoXN8OXO8jmZg/6fOvFnrGl6CBp8sioe5F3R023fDum546IqS8EZdCl5T0gW/boTbSV8luitab65xBO3PmUI+V2OEFCL6WcJxawBAoGBAOZoft6/LatdoXzr8vh+rKzacUHw246fpacbgx0B5DDymM7hbhXbY/NoCWPgBJtV3XI3DtMJ5yvlEVDQvPfbSHRPx2XQknwrM7ly2SLbaC+tuhcvoG6F1RLWFx+y/583seSlVNuWC9KdpLTKzo8wl8Z4/kheLTBxTxL20NZu79XBAoGBALd3fNoXk5V+T16hnSinPtt2NEsZpn+4w07DikzcpdyjCL5PYjp/BppmX3xly96fCZh3MO3Vkuya1xgauMzxVKQlR/aD5yVmsqK7wxNTY1ZQM74B44/4Mks/8MG2r7o3DElA4/qIeMP4CwkWmYcuij7npm2bgIqFzS+4aGZfDRF7AoGAKMO2Jpy2bMo9BwgLzdFDpbVkMmF1xu8R9NXWRayO/eX+CSQzQOS281qlxqjcx8rSSiHZmpb28notrRmxRTzjvchbo/TZ5eQS262pIxSkg0L+WJnRjZxaDWIZZz9ZIIdPDv/9WnhakSHZAS+cihLz12aSvqUC4744WkeWvUmVX0ECgYAGLDoCKHrps7c96tgbzwy5W4/E2xcUAwZnNwMHNQFLnBymMouOhkmVlk4uJEqosdcjzxbRWbc4yLjl8bg4BQKhBzQVojh7tKnb+c9Fbi/QbqBfCzc519LxXzRdgCUHceSy7kD9Y+wUQ9szMhR2TOWP2kFqPKolfvz5Vw4EK7yH0wKBgQDerq9Pthbii7lNt528/q0cH9vOMn9z76o6jMMea9EibclVHtdcQBWLOn8Yw97k+WSXYGuUrQUWWQbyabZqWkkS4cEjJf5/DiwOuYdNVXg7FK56ucTczBA7lR4dnunPW6U1HbSWf0Cn4Y/cl/z7B5QBSQt0W38IYHSaf6/sqsV6SA==', 'app_auth_token' => '', - 'app_public_cert_path' => __DIR__ . '/Cert/alipayAppPublicCert.crt', - 'alipay_public_cert_path' => __DIR__ . '/Cert/alipayPublicCert.crt', - 'alipay_root_cert_path' => __DIR__ . '/Cert/alipayRootCert.crt', + 'app_public_cert_path' => __DIR__.'/Cert/alipayAppPublicCert.crt', + 'alipay_public_cert_path' => __DIR__.'/Cert/alipayPublicCert.crt', + 'alipay_root_cert_path' => __DIR__.'/Cert/alipayRootCert.crt', 'notify_url' => 'https://pay.yansongda.cn', 'return_url' => 'https://pay.yansongda.cn', ], @@ -144,7 +151,26 @@ protected function setUp(): void 'mch_secret_key' => '979da4cfccbae7923641daa5dd7047c2', 'mode' => Pay::MODE_SANDBOX, ], - ] + ], + 'epay' => [ + 'default' => [ + // 服务代码 + 'svr_code' => '', + // 必填-合作商ID + 'partner_id' => '6a13eab71c4f4b0aa4757eda6fc59710', + // 必填-公私钥对编号 + 'public_key_code' => '00', + // 必填-商户私钥(加密签名) + 'mch_secret_cert_path' => __DIR__.'/Cert/EpayKey.pem', + // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) + 'mch_public_cert_path' => __DIR__.'/Cert/EpayCert.cer', + // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) + 'epay_public_cert_path' => __DIR__.'/Cert/jschina.cer', + 'notify_url' => 'http://127.0.0.1:8000/epay/return', + // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:test环境, + 'mode' => Pay::MODE_SANDBOX, + ], + ], ]; // hyperf 单测时,未在 hyperf 框架内,所以 sdk 没有 container, 手动设置一个 From 0d9d31dde63ac53f17316dcce4b17bebee3e240b Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 10:44:51 +0800 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Provider/EpayTest.php | 9 +---- tests/Shortcut/Epay/CancelShortcutTest.php | 40 ++++++++++++++++++++++ tests/Shortcut/Epay/QueryShortcutTest.php | 39 +++++++++++++++++++++ tests/Shortcut/Epay/RefundShortcutTest.php | 38 ++++++++++++++++++++ tests/Shortcut/Epay/ScanShortcutTest.php | 38 ++++++++++++++++++++ 5 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 tests/Shortcut/Epay/CancelShortcutTest.php create mode 100644 tests/Shortcut/Epay/QueryShortcutTest.php create mode 100644 tests/Shortcut/Epay/RefundShortcutTest.php create mode 100644 tests/Shortcut/Epay/ScanShortcutTest.php diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index 36e327da1..203e72096 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -32,14 +32,6 @@ public function testShortcutNotFound() Pay::epay()->foo(); } - public function testShortcutIncompatible() - { - self::expectException(InvalidParamsException::class); - self::expectExceptionCode(Exception::PARAMS_SHORTCUT_INVALID); - - Pay::wechat()->foo(); - } - public function testMergeCommonPlugins() { Pay::config([]); @@ -51,6 +43,7 @@ public function testMergeCommonPlugins() [AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class], ), Pay::epay()->mergeCommonPlugins($plugins)); } + public function testSuccess() { $result = Pay::epay()->success(); diff --git a/tests/Shortcut/Epay/CancelShortcutTest.php b/tests/Shortcut/Epay/CancelShortcutTest.php new file mode 100644 index 000000000..3be1e9cd5 --- /dev/null +++ b/tests/Shortcut/Epay/CancelShortcutTest.php @@ -0,0 +1,40 @@ +plugin = new CancelShortcut(); + } + + + public function testDefault() + { + self::assertEquals([ + StartPlugin::class, + CancelPlugin::class, + AddPayloadSignPlugin::class, + AddRadarPlugin::class, + VerifySignaturePlugin::class, + ResponsePlugin::class, + ParserPlugin::class, + ], $this->plugin->getPlugins([])); + } +} diff --git a/tests/Shortcut/Epay/QueryShortcutTest.php b/tests/Shortcut/Epay/QueryShortcutTest.php new file mode 100644 index 000000000..e5c1540e8 --- /dev/null +++ b/tests/Shortcut/Epay/QueryShortcutTest.php @@ -0,0 +1,39 @@ +plugin = new QueryShortcut(); + } + + public function testDefault() + { + self::assertEquals([ + StartPlugin::class, + QueryPlugin::class, + AddPayloadSignPlugin::class, + AddRadarPlugin::class, + VerifySignaturePlugin::class, + ResponsePlugin::class, + ParserPlugin::class, + ], $this->plugin->getPlugins([])); + } +} diff --git a/tests/Shortcut/Epay/RefundShortcutTest.php b/tests/Shortcut/Epay/RefundShortcutTest.php new file mode 100644 index 000000000..9e4facafc --- /dev/null +++ b/tests/Shortcut/Epay/RefundShortcutTest.php @@ -0,0 +1,38 @@ +plugin = new RefundShortcut(); + } + + public function testDefault() + { + self::assertEquals([ + StartPlugin::class, + RefundPlugin::class, + AddPayloadSignPlugin::class, + AddRadarPlugin::class, + VerifySignaturePlugin::class, + ResponsePlugin::class, + ParserPlugin::class, + ], $this->plugin->getPlugins([])); + } +} diff --git a/tests/Shortcut/Epay/ScanShortcutTest.php b/tests/Shortcut/Epay/ScanShortcutTest.php new file mode 100644 index 000000000..8beb61d71 --- /dev/null +++ b/tests/Shortcut/Epay/ScanShortcutTest.php @@ -0,0 +1,38 @@ +plugin = new ScanShortcut(); + } + + public function testDefault() + { + self::assertEquals([ + StartPlugin::class, + PrepayPlugin::class, + AddPayloadSignPlugin::class, + AddRadarPlugin::class, + VerifySignaturePlugin::class, + ResponsePlugin::class, + ParserPlugin::class, + ], $this->plugin->getPlugins([])); + } +} From f0f4c7c5f1017ae2d34ec58d9584f5080570464f Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 12:09:40 +0800 Subject: [PATCH 03/17] =?UTF-8?q?style:=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/AddPayloadSignPlugin.php | 1 + src/Plugin/Epay/CallbackPlugin.php | 8 ++++---- src/Plugin/Epay/Pay/Scan/QueryPlugin.php | 2 +- src/Plugin/Epay/StartPlugin.php | 4 ++-- src/Provider/Epay.php | 8 ++++---- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Plugin/Epay/AddPayloadSignPlugin.php b/src/Plugin/Epay/AddPayloadSignPlugin.php index 1ac5e64f8..04ba45ec9 100644 --- a/src/Plugin/Epay/AddPayloadSignPlugin.php +++ b/src/Plugin/Epay/AddPayloadSignPlugin.php @@ -13,6 +13,7 @@ use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; use Yansongda\Supports\Collection; + use function Yansongda\Pay\get_provider_config; class AddPayloadSignPlugin implements PluginInterface diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php index 8b417b0d7..12eb72ee3 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -6,12 +6,12 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\InvalidConfigException; +use Yansongda\Artful\Exception\InvalidParamsException; +use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; -use Yansongda\Pay\Exception\InvalidConfigException; -use Yansongda\Pay\Exception\InvalidParamsException; -use Yansongda\Pay\Exception\InvalidResponseException; use Yansongda\Supports\Collection; use function Yansongda\Pay\get_provider_config; @@ -60,7 +60,7 @@ protected function verifySign(array $config, Collection $payload, ?string $signa file_get_contents($publicCert) ); if (!$result) { - throw new InvalidResponseException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); + throw new InvalidConfigException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); } } diff --git a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php index 29daa29d5..defbd3711 100644 --- a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php +++ b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php @@ -4,8 +4,8 @@ namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; -use Yansongda\Pay\Plugin\Epay\GeneralPlugin; use Yansongda\Artful\Rocket; +use Yansongda\Pay\Plugin\Epay\GeneralPlugin; class QueryPlugin extends GeneralPlugin { diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Epay/StartPlugin.php index 74cddd120..e5234f6b7 100644 --- a/src/Plugin/Epay/StartPlugin.php +++ b/src/Plugin/Epay/StartPlugin.php @@ -6,11 +6,11 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Logger; +use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\ContainerException; use Yansongda\Pay\Exception\InvalidConfigException; use Yansongda\Pay\Exception\ServiceNotFoundException; -use Yansongda\Artful\Logger; -use Yansongda\Artful\Rocket; use Yansongda\Supports\Str; use function Yansongda\Pay\get_provider_config; diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php index 795208d28..e566fd0b9 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Epay.php @@ -16,16 +16,16 @@ use Yansongda\Artful\Plugin\ParserPlugin; use Yansongda\Artful\Rocket; use Yansongda\Pay\Contract\ProviderInterface; +use Yansongda\Pay\Event\CallbackReceived; +use Yansongda\Pay\Event\MethodCalled; +use Yansongda\Pay\Exception\Exception; +use Yansongda\Pay\Pay; use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; use Yansongda\Pay\Plugin\Epay\CallbackPlugin; use Yansongda\Pay\Plugin\Epay\ResponsePlugin; use Yansongda\Pay\Plugin\Epay\StartPlugin; use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; -use Yansongda\Pay\Event\CallbackReceived; -use Yansongda\Pay\Event\MethodCalled; -use Yansongda\Pay\Exception\Exception; -use Yansongda\Pay\Pay; use Yansongda\Supports\Collection; use Yansongda\Supports\Str; From 1f5f035ad68135e7c16843d39da6d7219ea86862 Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 15:34:35 +0800 Subject: [PATCH 04/17] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0epay=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B&=E4=BF=AE=E5=A4=8D=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=E5=8F=91=E7=8E=B0=E7=9A=84=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/CallbackPlugin.php | 6 +- src/Plugin/Epay/GeneralPlugin.php | 33 ------ src/Plugin/Epay/Pay/Scan/CancelPlugin.php | 15 --- src/Plugin/Epay/Pay/Scan/PrepayPlugin.php | 36 ++++--- src/Plugin/Epay/Pay/Scan/QueryPlugin.php | 18 ++-- src/Plugin/Epay/Pay/Scan/RefundPlugin.php | 18 ++-- src/Plugin/Epay/StartPlugin.php | 3 +- src/Provider/Epay.php | 8 +- src/Shortcut/Epay/CancelShortcut.php | 30 ------ tests/Provider/EpayTest.php | 118 ++++++++++++++++++++++ 10 files changed, 165 insertions(+), 120 deletions(-) delete mode 100644 src/Plugin/Epay/GeneralPlugin.php delete mode 100644 src/Plugin/Epay/Pay/Scan/CancelPlugin.php delete mode 100644 src/Shortcut/Epay/CancelShortcut.php diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php index 12eb72ee3..7496a31e2 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -6,6 +6,7 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Direction\NoHttpRequestDirection; use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\InvalidParamsException; use Yansongda\Artful\Exception\InvalidResponseException; @@ -25,7 +26,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $this->formatRequestAndParams($rocket); $params = $rocket->getParams(); - $config = get_provider_config('alipay', $params); + $config = get_provider_config('epay', $params); $payload = $rocket->getPayload(); $signature = $payload->get('sign'); @@ -35,7 +36,8 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $this->verifySign($config, $payload, $signature); - $rocket->setDestination($rocket->getPayload()); + $rocket->setDirection(NoHttpRequestDirection::class) + ->setDestination($rocket->getPayload()); Logger::info('[epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); diff --git a/src/Plugin/Epay/GeneralPlugin.php b/src/Plugin/Epay/GeneralPlugin.php deleted file mode 100644 index 75e7701fa..000000000 --- a/src/Plugin/Epay/GeneralPlugin.php +++ /dev/null @@ -1,33 +0,0 @@ - $rocket]); - - $this->doSomethingBefore($rocket); - - $rocket->setPacker(QueryPacker::class)->mergePayload([ - 'service' => $this->getService(), - ]); - - Logger::info('[epay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]); - - return $next($rocket); - } - - protected function doSomethingBefore(Rocket $rocket): void {} - - abstract protected function getService(): string; -} diff --git a/src/Plugin/Epay/Pay/Scan/CancelPlugin.php b/src/Plugin/Epay/Pay/Scan/CancelPlugin.php deleted file mode 100644 index f84284679..000000000 --- a/src/Plugin/Epay/Pay/Scan/CancelPlugin.php +++ /dev/null @@ -1,15 +0,0 @@ - $rocket]); - /** - * @throws ServiceNotFoundException - * @throws ContainerException - */ - protected function doSomethingBefore(Rocket $rocket): void - { - if (!empty($rocket->getPayload()['notify_url'])) { - return; - } + $backUrl = $rocket->getPayload()['notify_url'] ?? null; + if (empty($backUrl)) { + $params = $rocket->getParams(); + $config = get_provider_config('epay', $params); - $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $backUrl = $config['notify_url'] ?? null; + } - $backUrl = $config['notify_url'] ?? null; if (!$backUrl) { throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [notify_url]'); } $rocket->mergePayload([ + 'service' => 'atPay', 'backUrl' => $backUrl, ]); + + Logger::info('[Epay][Pay][Scan][PrepayPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); } } diff --git a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php index defbd3711..428619096 100644 --- a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php +++ b/src/Plugin/Epay/Pay/Scan/QueryPlugin.php @@ -4,20 +4,24 @@ namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; +use Closure; +use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; -use Yansongda\Pay\Plugin\Epay\GeneralPlugin; -class QueryPlugin extends GeneralPlugin +class QueryPlugin implements PluginInterface { - protected function getService(): string + public function assembly(Rocket $rocket, Closure $next): Rocket { - return 'payCheck'; - } + Logger::debug('[Epay][Pay][Scan][QueryPlugin] 插件开始装载', ['rocket' => $rocket]); - protected function doSomethingBefore(Rocket $rocket): void - { $rocket->mergePayload([ 'deviceNo' => '1234567890', + 'service' => 'payCheck', ]); + + Logger::info('[Epay][Pay][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); } } diff --git a/src/Plugin/Epay/Pay/Scan/RefundPlugin.php b/src/Plugin/Epay/Pay/Scan/RefundPlugin.php index e2aab8a03..d06a02f75 100644 --- a/src/Plugin/Epay/Pay/Scan/RefundPlugin.php +++ b/src/Plugin/Epay/Pay/Scan/RefundPlugin.php @@ -4,20 +4,24 @@ namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; +use Closure; +use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; -use Yansongda\Pay\Plugin\Epay\GeneralPlugin; -class RefundPlugin extends GeneralPlugin +class RefundPlugin implements PluginInterface { - protected function getService(): string + public function assembly(Rocket $rocket, Closure $next): Rocket { - return 'payRefund'; - } + Logger::debug('[Epay][Pay][Scan][RefundPlugin] 插件开始装载', ['rocket' => $rocket]); - protected function doSomethingBefore(Rocket $rocket): void - { $rocket->mergePayload([ 'deviceNo' => '1234567890', + 'service' => 'payRefund', ]); + + Logger::info('[Epay][Pay][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); } } diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Epay/StartPlugin.php index e5234f6b7..60818c4d3 100644 --- a/src/Plugin/Epay/StartPlugin.php +++ b/src/Plugin/Epay/StartPlugin.php @@ -7,6 +7,7 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; use Yansongda\Artful\Logger; +use Yansongda\Artful\Packer\QueryPacker; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\ContainerException; use Yansongda\Pay\Exception\InvalidConfigException; @@ -29,7 +30,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $params = $rocket->getParams(); $config = get_provider_config('epay', $params); - $rocket->mergePayload(array_merge( + $rocket->setPacker(QueryPacker::class)->mergePayload(array_merge( $params, [ 'createData' => date('Ymd'), diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php index e566fd0b9..7c841510d 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Epay.php @@ -66,11 +66,7 @@ public function mergeCommonPlugins(array $plugins): array public function cancel($order): Collection|Rocket { - $order = is_array($order) ? $order : ['outTradeNo' => $order]; - - Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); - - return $this->__call('cancel', [$order]); + throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); } public function close($order): Collection|Rocket @@ -89,7 +85,7 @@ public function callback(null|array|ServerRequestInterface $contents = null, ?ar { $request = $this->getCallbackParams($contents); - Event::dispatch(new CallbackReceived('epay', $request, $params, null)); + Event::dispatch(new CallbackReceived('epay', $request->all(), $params, null)); return $this->pay( [CallbackPlugin::class], diff --git a/src/Shortcut/Epay/CancelShortcut.php b/src/Shortcut/Epay/CancelShortcut.php deleted file mode 100644 index 901029e9e..000000000 --- a/src/Shortcut/Epay/CancelShortcut.php +++ /dev/null @@ -1,30 +0,0 @@ -mergeCommonPlugins($plugins)); } + public function testScan() + { + $response = 'errCode=&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + //// + $http = Mockery::mock(Client::class); + $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); + Pay::set(HttpClientInterface::class, $http); + + $result = Pay::epay()->scan([ + 'outTradeNo' => 'YC202406170003', + 'totalFee' => 0.01, + 'proInfo' => '元仓充值', + 'backUrl' => 'http://47.242.204.179:8000/api/user/recharge/ePayNotice', + ]); + + self::assertArrayHasKey('payUrl', $result->all()); + } + + public function testQuery() + { + $response = 'deviceNo=1234567890&errCode=&errMsg=&field1=2&field2=&field3=&orderNo=20240617144526400259379&orderStatus=2&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&signType=RSA&sign=fRMelfVITd0+aV+I5MT9SyTwRjWB+vOVyES9s3l+eKFV9bXwtQLpaORFpr1emepm2mZjCAgK9AORaYYn9vhk+0x+b2jk2QiyQ3aXYrrYx0+foK/OqN9dcJjSTIIpUUitGYk/6CJwe0OFPsDWgqDiLb9A298VFXg++czErz0stcM='; + $http = Mockery::mock(Client::class); + $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); + Pay::set(HttpClientInterface::class, $http); + + $outTradeNo = 'YC202406170003'; + $result = Pay::epay()->query([ + 'outTradeNo' => $outTradeNo, + ]); + + self::assertArrayHasKey('orderNo', $result->all()); + self::assertArrayHasKey('orderStatus', $result->all()); + self::assertEquals($outTradeNo, $result->get('outTradeNo')); + } + + public function testRefundErrorRefundAmt() + { + //单元测试检测异常是否正确 + $this->expectException(InvalidResponseException::class); + $response = 'field1=&field2=&field3=&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&respBizDate=20240617&respCode=027111&respMsg=退款金额错误&signType=RSA&sign=hzreuJgx+iYz71W8HKyQEH4+XqE7c2Ad6NLa8lSJcVEmjc2nZPZl2s+mBVmZX3PYSlysqq5rlXysGMzQxf4CWkuoUK9wGsCDUIssCBOPcRjdsC2/uFaLavs/jagkKE/tLt45D6h4kibgaHIZabN5NgUkP0p0TAFHISsPPdqjKLY='; + $http = Mockery::mock(Client::class); + $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); + Pay::set(HttpClientInterface::class, $http); + $outTradeNo = 'YC202406170003'; + Pay::epay()->refund([ + 'outTradeNo' => $outTradeNo, + 'refundAmt' => 0.02, + ]); + dd($result); + } + + public function testRefund() + { + //单元测试检测异常是否正确 + $response = 'fee=0&field1=2&field2=&field3=&orderStatus=3&outRefundNo=RK-YC202406170004&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&refundAmt=0.01&refundNo=20240617151229401202589&respBizDate=20240617&respCode=000000&respMsg=交易成功&signType=RSA&sign=VJi8vD3ZkcPZXkgkJ3RX9oREKxeNoUAi9+SZoiBHNlNc87QN0NRngmLthHbzJUV6Fz8hQX5jumZQnhTpEqlTgEZsHRCIm7ZsqhingBNVKItq/sAqzlpIeogU/jVE4zueqInYIMbVOj6+3AQyZ1+Tblz6d0JrGals3exmBUt/03U='; + $http = Mockery::mock(Client::class); + $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); + Pay::set(HttpClientInterface::class, $http); + + $outRefundNo = 'RK-YC202406170004'; + $result = Pay::epay()->refund([ + 'outTradeNo' => 'YC202406170004', + 'refundAmt' => 0.01, + 'outRefundNo' => $outRefundNo, + ]); + + self::assertArrayHasKey('orderStatus', $result->all()); + self::assertEquals($outRefundNo, $result->get('outRefundNo')); + dd($result); + } + + + public function testCancel() + { + $this->expectException(InvalidParamsException::class); + + Pay::epay()->cancel([ + 'outTradeNo' => 'YC202406170003', + ]); + } + + public function testClose() + { + $this->expectException(InvalidParamsException::class); + + Pay::epay()->cancel([ + 'outTradeNo' => 'YC202406170003', + ]); + } + + public function testCallback() + { + $playload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $result = Pay::epay()->callback($playload); + self::assertNotEmpty($result->all()); + self::assertArrayHasKey('outTradeNo', $result->all()); + self::assertArrayHasKey('orderNo', $result->all()); + self::assertArrayHasKey('orderStatus', $result->all()); + self::assertArrayHasKey('field3', $result->all()); + + } + public function testSuccess() { $result = Pay::epay()->success(); From 8986aa3c1beb524a370e7008c76926468ada787e Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 15:40:13 +0800 Subject: [PATCH 05/17] =?UTF-8?q?style:=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/CallbackPlugin.php | 2 +- src/Provider/Epay.php | 2 +- tests/Provider/EpayTest.php | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php index 7496a31e2..aad14e0d9 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -37,7 +37,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $this->verifySign($config, $payload, $signature); $rocket->setDirection(NoHttpRequestDirection::class) - ->setDestination($rocket->getPayload()); + ->setDestination($rocket->getPayload()); Logger::info('[epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php index 7c841510d..529cc9eef 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Epay.php @@ -66,7 +66,7 @@ public function mergeCommonPlugins(array $plugins): array public function cancel($order): Collection|Rocket { - throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); + throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); } public function close($order): Collection|Rocket diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index 82d342014..729069686 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -97,7 +97,6 @@ public function testRefundErrorRefundAmt() 'outTradeNo' => $outTradeNo, 'refundAmt' => 0.02, ]); - dd($result); } public function testRefund() @@ -117,7 +116,6 @@ public function testRefund() self::assertArrayHasKey('orderStatus', $result->all()); self::assertEquals($outRefundNo, $result->get('outRefundNo')); - dd($result); } From 1de4febe59f7db18f23e2e5a18caa51a9737a50d Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Mon, 17 Jun 2024 15:57:52 +0800 Subject: [PATCH 06/17] =?UTF-8?q?test:=20=E5=88=A0=E9=99=A4epay=E7=9A=84?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Shortcut/Epay/CancelShortcutTest.php | 40 ---------------------- 1 file changed, 40 deletions(-) delete mode 100644 tests/Shortcut/Epay/CancelShortcutTest.php diff --git a/tests/Shortcut/Epay/CancelShortcutTest.php b/tests/Shortcut/Epay/CancelShortcutTest.php deleted file mode 100644 index 3be1e9cd5..000000000 --- a/tests/Shortcut/Epay/CancelShortcutTest.php +++ /dev/null @@ -1,40 +0,0 @@ -plugin = new CancelShortcut(); - } - - - public function testDefault() - { - self::assertEquals([ - StartPlugin::class, - CancelPlugin::class, - AddPayloadSignPlugin::class, - AddRadarPlugin::class, - VerifySignaturePlugin::class, - ResponsePlugin::class, - ParserPlugin::class, - ], $this->plugin->getPlugins([])); - } -} From 666d37fbfd6f39e74f4b84870ceadbe8d05a5dda Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 09:09:46 +0800 Subject: [PATCH 07/17] =?UTF-8?q?pref:=20phpstan=20error=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/AddRadarPlugin.php | 6 ++++++ src/Plugin/Epay/CallbackPlugin.php | 13 +++++++++++++ src/Plugin/Epay/Pay/Scan/PrepayPlugin.php | 7 +++++++ src/Plugin/Epay/StartPlugin.php | 8 +++----- src/Plugin/Epay/VerifySignaturePlugin.php | 12 ++++++++++++ src/Provider/Epay.php | 14 +++++++++++--- src/Shortcut/Epay/RefundShortcut.php | 4 ---- 7 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/Plugin/Epay/AddRadarPlugin.php b/src/Plugin/Epay/AddRadarPlugin.php index bb23e4e9f..db230416a 100644 --- a/src/Plugin/Epay/AddRadarPlugin.php +++ b/src/Plugin/Epay/AddRadarPlugin.php @@ -7,6 +7,8 @@ use Closure; use GuzzleHttp\Psr7\Request; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\ContainerException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Supports\Collection; @@ -16,6 +18,10 @@ class AddRadarPlugin implements PluginInterface { + /** + * @throws ServiceNotFoundException + * @throws ContainerException + */ public function assembly(Rocket $rocket, Closure $next): Rocket { Logger::info('[epay][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]); diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php index aad14e0d9..92b7e63e5 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -7,9 +7,11 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; use Yansongda\Artful\Direction\NoHttpRequestDirection; +use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\InvalidParamsException; use Yansongda\Artful\Exception\InvalidResponseException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; @@ -19,6 +21,13 @@ class CallbackPlugin implements PluginInterface { + /** + * @throws ServiceNotFoundException + * @throws InvalidResponseException + * @throws InvalidConfigException + * @throws ContainerException + * @throws InvalidParamsException + */ public function assembly(Rocket $rocket, Closure $next): Rocket { Logger::info('[epay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]); @@ -44,6 +53,10 @@ public function assembly(Rocket $rocket, Closure $next): Rocket return $next($rocket); } + /** + * @throws InvalidConfigException + * @throws InvalidResponseException + */ protected function verifySign(array $config, Collection $payload, ?string $signature = null): void { if (!$signature) { diff --git a/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php b/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php index ca99c2dc5..d1c9fecee 100644 --- a/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php +++ b/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php @@ -6,7 +6,9 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\InvalidConfigException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; @@ -15,6 +17,11 @@ class PrepayPlugin implements PluginInterface { + /** + * @throws InvalidConfigException + * @throws ServiceNotFoundException + * @throws ContainerException + */ public function assembly(Rocket $rocket, Closure $next): Rocket { Logger::debug('[Epay][Pay][Scan][PrepayPlugin] 插件开始装载', ['rocket' => $rocket]); diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Epay/StartPlugin.php index 60818c4d3..dd77e1f09 100644 --- a/src/Plugin/Epay/StartPlugin.php +++ b/src/Plugin/Epay/StartPlugin.php @@ -6,12 +6,11 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\ContainerException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Packer\QueryPacker; use Yansongda\Artful\Rocket; -use Yansongda\Pay\Exception\ContainerException; -use Yansongda\Pay\Exception\InvalidConfigException; -use Yansongda\Pay\Exception\ServiceNotFoundException; use Yansongda\Supports\Str; use function Yansongda\Pay\get_provider_config; @@ -19,9 +18,8 @@ class StartPlugin implements PluginInterface { /** - * @throws ContainerException * @throws ServiceNotFoundException - * @throws InvalidConfigException + * @throws ContainerException */ public function assembly(Rocket $rocket, Closure $next): Rocket { diff --git a/src/Plugin/Epay/VerifySignaturePlugin.php b/src/Plugin/Epay/VerifySignaturePlugin.php index e268e49fc..0e7735630 100644 --- a/src/Plugin/Epay/VerifySignaturePlugin.php +++ b/src/Plugin/Epay/VerifySignaturePlugin.php @@ -6,8 +6,10 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\InvalidResponseException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; @@ -20,6 +22,12 @@ class VerifySignaturePlugin implements PluginInterface { + /** + * @throws ServiceNotFoundException + * @throws InvalidConfigException + * @throws InvalidResponseException + * @throws ContainerException + */ public function assembly(Rocket $rocket, Closure $next): Rocket { /* @var Rocket $rocket */ @@ -39,6 +47,10 @@ public function assembly(Rocket $rocket, Closure $next): Rocket return $rocket; } + /** + * @throws InvalidResponseException + * @throws InvalidConfigException + */ protected function verifySign(array $config, string $body): void { // 解析签名 diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php index 529cc9eef..0920fd3b0 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Epay.php @@ -41,7 +41,7 @@ class Epay implements ProviderInterface public function __call($name, $params) { - $plugin = '\\Yansongda\\Pay\\Shortcut\\Epay\\'.Str::studly($name).'Shortcut'; + $plugin = '\Yansongda\Pay\Shortcut\Epay\\'.Str::studly($name).'Shortcut'; return Artful::shortcut($plugin, ...$params); } @@ -64,11 +64,17 @@ public function mergeCommonPlugins(array $plugins): array ); } + /** + * @throws InvalidParamsException + */ public function cancel($order): Collection|Rocket { throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); } + /** + * @throws InvalidParamsException + */ public function close($order): Collection|Rocket { throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support close api'); @@ -81,6 +87,10 @@ public function refund(array $order): Collection|Rocket return $this->__call('refund', [$order]); } + /** + * @throws ContainerException + * @throws InvalidParamsException + */ public function callback(null|array|ServerRequestInterface $contents = null, ?array $params = null): Collection|Rocket { $request = $this->getCallbackParams($contents); @@ -104,8 +114,6 @@ public function success(): ResponseInterface public function query(array $order): Collection|Rocket { - $order = is_array($order) ? $order : ['outTradeNo' => $order]; - Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); return $this->__call('query', [$order]); diff --git a/src/Shortcut/Epay/RefundShortcut.php b/src/Shortcut/Epay/RefundShortcut.php index 0065e119d..56e0f5c1e 100644 --- a/src/Shortcut/Epay/RefundShortcut.php +++ b/src/Shortcut/Epay/RefundShortcut.php @@ -6,7 +6,6 @@ use Yansongda\Artful\Contract\ShortcutInterface; use Yansongda\Artful\Plugin\ParserPlugin; -use Yansongda\Pay\Exception\InvalidParamsException; use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; use Yansongda\Pay\Plugin\Epay\Pay\Scan\RefundPlugin; @@ -16,9 +15,6 @@ class RefundShortcut implements ShortcutInterface { - /** - * @throws InvalidParamsException - */ public function getPlugins(array $params): array { return [ From 795b1d4ee8ac929955b7c5a0d577d7dd72ba2de7 Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 10:37:33 +0800 Subject: [PATCH 08/17] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/AddPayloadSignPlugin.php | 4 +- src/Plugin/Epay/AddRadarPlugin.php | 4 +- src/Plugin/Epay/CallbackPlugin.php | 4 +- src/Plugin/Epay/ResponsePlugin.php | 10 ++-- src/Plugin/Epay/StartPlugin.php | 4 +- src/Plugin/Epay/VerifySignaturePlugin.php | 16 ++---- src/Provider/Epay.php | 4 +- .../Plugin/Epay/AddPayloadSignPluginTest.php | 31 +++++++++++ tests/Plugin/Epay/AddRadarPluginTest.php | 33 +++++++++++ tests/Plugin/Epay/CallbackPluginTest.php | 47 ++++++++++++++++ .../Plugin/Epay/Pay/Scan/PrepayPluginTest.php | 29 ++++++++++ .../Plugin/Epay/Pay/Scan/QueryPluginTest.php | 29 ++++++++++ .../Plugin/Epay/Pay/Scan/RefundPluginTest.php | 29 ++++++++++ tests/Plugin/Epay/ResponsePluginTest.php | 55 +++++++++++++++++++ tests/Plugin/Epay/StartPluginTest.php | 35 ++++++++++++ .../Plugin/Epay/VerifySignaturePluginTest.php | 42 ++++++++++++++ tests/Provider/EpayTest.php | 4 +- 17 files changed, 353 insertions(+), 27 deletions(-) create mode 100644 tests/Plugin/Epay/AddPayloadSignPluginTest.php create mode 100644 tests/Plugin/Epay/AddRadarPluginTest.php create mode 100644 tests/Plugin/Epay/CallbackPluginTest.php create mode 100644 tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php create mode 100644 tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php create mode 100644 tests/Plugin/Epay/Pay/Scan/RefundPluginTest.php create mode 100644 tests/Plugin/Epay/ResponsePluginTest.php create mode 100644 tests/Plugin/Epay/StartPluginTest.php create mode 100644 tests/Plugin/Epay/VerifySignaturePluginTest.php diff --git a/src/Plugin/Epay/AddPayloadSignPlugin.php b/src/Plugin/Epay/AddPayloadSignPlugin.php index 04ba45ec9..cc6954a37 100644 --- a/src/Plugin/Epay/AddPayloadSignPlugin.php +++ b/src/Plugin/Epay/AddPayloadSignPlugin.php @@ -25,7 +25,7 @@ class AddPayloadSignPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[epay][AddPayloadSignPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Epay][AddPayloadSignPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); $config = get_provider_config('epay', $params); @@ -42,7 +42,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket 'sign' => $sign, ]); - Logger::info('[epay][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Epay][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/AddRadarPlugin.php b/src/Plugin/Epay/AddRadarPlugin.php index db230416a..fa98ad02a 100644 --- a/src/Plugin/Epay/AddRadarPlugin.php +++ b/src/Plugin/Epay/AddRadarPlugin.php @@ -24,7 +24,7 @@ class AddRadarPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[epay][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Epay][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); $config = get_provider_config('epay', $params); @@ -37,7 +37,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $this->getBody($payload), )); - Logger::info('[epay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Epay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Epay/CallbackPlugin.php index 92b7e63e5..83e1c3632 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Epay/CallbackPlugin.php @@ -30,7 +30,7 @@ class CallbackPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[epay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Epay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]); $this->formatRequestAndParams($rocket); @@ -48,7 +48,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $rocket->setDirection(NoHttpRequestDirection::class) ->setDestination($rocket->getPayload()); - Logger::info('[epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/ResponsePlugin.php b/src/Plugin/Epay/ResponsePlugin.php index d0d1ba83e..03a9096f4 100644 --- a/src/Plugin/Epay/ResponsePlugin.php +++ b/src/Plugin/Epay/ResponsePlugin.php @@ -7,10 +7,10 @@ use Closure; use Psr\Http\Message\ResponseInterface; use Yansongda\Artful\Contract\PluginInterface; +use Yansongda\Artful\Exception\Exception; use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; -use Yansongda\Pay\Exception\Exception; use Yansongda\Supports\Collection; class ResponsePlugin implements PluginInterface @@ -23,11 +23,11 @@ public function assembly(Rocket $rocket, Closure $next): Rocket /* @var Rocket $rocket */ $rocket = $next($rocket); - Logger::debug('[Wechat][ResponsePlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::debug('[Epay][ResponsePlugin] 插件开始装载', ['rocket' => $rocket]); $this->validateResponse($rocket); - Logger::info('[Wechat][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Epay][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); return $rocket; } @@ -41,11 +41,11 @@ protected function validateResponse(Rocket $rocket): void $destinationOrigin = $rocket->getDestinationOrigin(); if ($destinationOrigin instanceof ResponseInterface && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) { - throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, 'epay返回状态码异常,请检查参数是否错误', $rocket->getDestination()); + throw new InvalidResponseException(Exception::RESPONSE_ERROR, 'epay返回状态码异常,请检查参数是否错误', $rocket->getDestination()); } if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) { - throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, sprintf('Epay返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); + throw new InvalidResponseException(Exception::RESPONSE_ERROR, sprintf('epay返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); } } } diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Epay/StartPlugin.php index dd77e1f09..902af5c48 100644 --- a/src/Plugin/Epay/StartPlugin.php +++ b/src/Plugin/Epay/StartPlugin.php @@ -23,7 +23,7 @@ class StartPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[epay][StartPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Epay][StartPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); $config = get_provider_config('epay', $params); @@ -43,7 +43,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket 'charset' => 'utf-8', ] )); - Logger::info('[epay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Epay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/VerifySignaturePlugin.php b/src/Plugin/Epay/VerifySignaturePlugin.php index 0e7735630..c25ad4190 100644 --- a/src/Plugin/Epay/VerifySignaturePlugin.php +++ b/src/Plugin/Epay/VerifySignaturePlugin.php @@ -7,12 +7,11 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; use Yansongda\Artful\Exception\ContainerException; -use Yansongda\Artful\Exception\InvalidConfigException; -use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; +use Yansongda\Pay\Exception\InvalidSignException; use Yansongda\Supports\Arr; use Yansongda\Supports\Collection; use Yansongda\Supports\Str; @@ -24,9 +23,7 @@ class VerifySignaturePlugin implements PluginInterface { /** * @throws ServiceNotFoundException - * @throws InvalidConfigException - * @throws InvalidResponseException - * @throws ContainerException + * @throws ContainerException|InvalidSignException */ public function assembly(Rocket $rocket, Closure $next): Rocket { @@ -48,8 +45,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket } /** - * @throws InvalidResponseException - * @throws InvalidConfigException + * @throws InvalidSignException */ protected function verifySign(array $config, string $body): void { @@ -57,12 +53,12 @@ protected function verifySign(array $config, string $body): void $signatureData = $this->getSignatureData($body); if (!$signatureData['sign']) { - throw new InvalidResponseException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Verify Epay Response Sign Failed: sign is empty', $body); + throw new InvalidSignException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, 'Verify Epay Response Sign Failed: sign is empty', $body); } $publicCert = $config['epay_public_cert_path'] ?? null; if (empty($publicCert)) { - throw new InvalidConfigException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Missing Epay Config -- [epay_public_cert_path]'); + throw new InvalidSignException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Missing Epay Config -- [epay_public_cert_path]'); } $result = 1 === openssl_verify( $signatureData['data'], @@ -70,7 +66,7 @@ protected function verifySign(array $config, string $body): void file_get_contents($publicCert) ); if (!$result) { - throw new InvalidResponseException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Verify Epay Response Sign Failed', func_get_args()); + throw new InvalidSignException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); } } diff --git a/src/Provider/Epay.php b/src/Provider/Epay.php index 0920fd3b0..17b9d2912 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Epay.php @@ -67,7 +67,7 @@ public function mergeCommonPlugins(array $plugins): array /** * @throws InvalidParamsException */ - public function cancel($order): Collection|Rocket + public function cancel(array $order): Collection|Rocket { throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); } @@ -75,7 +75,7 @@ public function cancel($order): Collection|Rocket /** * @throws InvalidParamsException */ - public function close($order): Collection|Rocket + public function close(array $order): Collection|Rocket { throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support close api'); } diff --git a/tests/Plugin/Epay/AddPayloadSignPluginTest.php b/tests/Plugin/Epay/AddPayloadSignPluginTest.php new file mode 100644 index 000000000..823827dc9 --- /dev/null +++ b/tests/Plugin/Epay/AddPayloadSignPluginTest.php @@ -0,0 +1,31 @@ +plugin = new AddPayloadSignPlugin(); + } + + public function testSignNormal() + { + $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'元仓充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; + $sign = "bDVcd91eQNVMyf7on1/YewqyNzBdorHy3/BA7L89CKuyDZtf/FId/GbGTBbn1QzGsLB8Vcultv1BxFrMGJUrEF73HO4rHtkAXKu9hna6KmtDCXRVNJw6fjuU9epXdJqE1RSB8f0j5HKDOxD2LBSZB1ZAbmH5v2WMCvx83cYpjOQ="; + $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')); + } +} diff --git a/tests/Plugin/Epay/AddRadarPluginTest.php b/tests/Plugin/Epay/AddRadarPluginTest.php new file mode 100644 index 000000000..07cae3fc0 --- /dev/null +++ b/tests/Plugin/Epay/AddRadarPluginTest.php @@ -0,0 +1,33 @@ +plugin = new AddRadarPlugin(); + } + + + public function testRadarPostNormal() + { + $rocket = new Rocket(); + $rocket->setParams([])->setPayload(new Collection(['name' => 'yansongda'])); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + + self::assertEquals('https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', (string) $result->getRadar()->getUri()); + self::stringContains('name=yansongda', (string) $result->getRadar()->getBody()); + self::assertEquals('POST', $result->getRadar()->getMethod()); + } +} diff --git a/tests/Plugin/Epay/CallbackPluginTest.php b/tests/Plugin/Epay/CallbackPluginTest.php new file mode 100644 index 000000000..cff4fc3e1 --- /dev/null +++ b/tests/Plugin/Epay/CallbackPluginTest.php @@ -0,0 +1,47 @@ +plugin = new CallbackPlugin(); + } + + public function testNormal() + { + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $request = new ServerRequest('POST', 'http://localhost'); + $request = $request->withParsedBody($payload); + + $rocket = new Rocket(); + $rocket->setParams(['request'=>Collection::wrap($request->getParsedBody())]); + + $result = $this->plugin->assembly($rocket, function ($rocket) {return $rocket;}); + + self::assertNotEmpty($result->getPayload()->all()); + } + +} diff --git a/tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php b/tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php new file mode 100644 index 000000000..6b5bfa0f5 --- /dev/null +++ b/tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php @@ -0,0 +1,29 @@ +plugin = new PrepayPlugin(); + } + + public function testNormal() + { + $rocket = (new Rocket()) + ->setParams([]); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertStringContainsString('atPay', $result->getPayload()->toJson()); + } +} diff --git a/tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php b/tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php new file mode 100644 index 000000000..4c4676a70 --- /dev/null +++ b/tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php @@ -0,0 +1,29 @@ +plugin = new QueryPlugin(); + } + + public function testNormal() + { + $rocket = (new Rocket()) + ->setParams([]); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertStringContainsString('payCheck', $result->getPayload()->toJson()); + self::assertStringContainsString('deviceNo', $result->getPayload()->toJson()); + } +} diff --git a/tests/Plugin/Epay/Pay/Scan/RefundPluginTest.php b/tests/Plugin/Epay/Pay/Scan/RefundPluginTest.php new file mode 100644 index 000000000..77721f00e --- /dev/null +++ b/tests/Plugin/Epay/Pay/Scan/RefundPluginTest.php @@ -0,0 +1,29 @@ +plugin = new RefundPlugin(); + } + + public function testNormal() + { + $rocket = (new Rocket()) + ->setParams([]); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertStringContainsString('payRefund', $result->getPayload()->toJson()); + self::assertStringContainsString('deviceNo', $result->getPayload()->toJson()); + } +} diff --git a/tests/Plugin/Epay/ResponsePluginTest.php b/tests/Plugin/Epay/ResponsePluginTest.php new file mode 100644 index 000000000..c7dc28705 --- /dev/null +++ b/tests/Plugin/Epay/ResponsePluginTest.php @@ -0,0 +1,55 @@ +plugin = new ResponsePlugin(); + } + + public function testNormal() + { + $body = 'errCode=&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + + public function testCodeWrong() + { + self::expectException(InvalidResponseException::class); + self::expectExceptionCode(Exception::RESPONSE_ERROR); + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000001&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + + public function testHttpWrong() + { + self::expectException(InvalidResponseException::class); + self::expectExceptionCode(Exception::RESPONSE_ERROR); + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } +} diff --git a/tests/Plugin/Epay/StartPluginTest.php b/tests/Plugin/Epay/StartPluginTest.php new file mode 100644 index 000000000..6398c893b --- /dev/null +++ b/tests/Plugin/Epay/StartPluginTest.php @@ -0,0 +1,35 @@ +plugin = new StartPlugin(); + } + + public function testNormal() + { + $rocket = new Rocket(); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + $payload = $result->getPayload(); + self::assertEquals('6a13eab71c4f4b0aa4757eda6fc59710', $payload->get('partnerId')); + self::assertEquals('v1.0.0', $payload->get('version')); + self::assertEquals('utf-8', $payload->get('charset')); + self::assertEquals(date('Ymd'), $payload->get('createData')); + self::assertEquals(date('His'), $payload->get('createTime')); + self::assertEquals(date('Ymd'), $payload->get('bizDate')); + self::assertEquals(QueryPacker::class, $result->getPacker()); + } +} diff --git a/tests/Plugin/Epay/VerifySignaturePluginTest.php b/tests/Plugin/Epay/VerifySignaturePluginTest.php new file mode 100644 index 000000000..513414860 --- /dev/null +++ b/tests/Plugin/Epay/VerifySignaturePluginTest.php @@ -0,0 +1,42 @@ +plugin = new VerifySignaturePlugin(); + } + + public function testSignNormal() + { + $body = 'errCode=&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + + public function testSignWrong() + { + self::expectException(InvalidSignException::class); + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } +} diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index 729069686..d6ea5d65d 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -139,7 +139,7 @@ public function testClose() public function testCallback() { - $playload = [ + $payload = [ 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', 'orderStatus'=> '1', 'totalFee'=> '0.02', @@ -151,7 +151,7 @@ public function testCallback() 'signType'=> 'RSA', 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' ]; - $result = Pay::epay()->callback($playload); + $result = Pay::epay()->callback($payload); self::assertNotEmpty($result->all()); self::assertArrayHasKey('outTradeNo', $result->all()); self::assertArrayHasKey('orderNo', $result->all()); From 5994214708fc7284e0d2c476a2c2c782523e98de Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 10:50:43 +0800 Subject: [PATCH 09/17] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Functions.php | 1 - tests/FunctionTest.php | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Functions.php b/src/Functions.php index 14b9ff461..7c6603d12 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -596,7 +596,6 @@ function verify_unipay_sign_qra(array $config, array $destination): void function get_epay_url(array $config, ?Collection $payload): string { $url = get_radar_url($config, $payload) ?? ''; - if (str_starts_with($url, 'http')) { return $url; } diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php index 2ecc80619..6a88ceec5 100644 --- a/tests/FunctionTest.php +++ b/tests/FunctionTest.php @@ -46,6 +46,7 @@ use function Yansongda\Pay\verify_unipay_sign_qra; use function Yansongda\Pay\verify_wechat_sign; use function Yansongda\Pay\verify_wechat_sign_v2; +use function Yansongda\Pay\get_epay_url; class FunctionTest extends TestCase { @@ -685,4 +686,11 @@ public function testVerifyUnipaySignQraEmpty() verify_unipay_sign_qra(get_provider_config('unipay', ['_config' => 'qra']), $payload); } + + public function testGetEpayUrl() + { + self::assertEquals('https://yansongda.cn', get_epay_url([], new Collection(['_url' => 'https://yansongda.cn']))); + self::assertEquals('https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm', get_epay_url(['mode' => Pay::MODE_NORMAL], new Collection())); + self::assertEquals('https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', get_epay_url(['mode' => Pay::MODE_SANDBOX], new Collection())); + } } From 72738dcf5307250f4d56e0b08a117c7c51a30a73 Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 15:12:16 +0800 Subject: [PATCH 10/17] =?UTF-8?q?docs=EF=BC=9Aepay=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0&=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/AddPayloadSignPlugin.php | 5 ++- src/Plugin/Epay/VerifySignaturePlugin.php | 21 ++-------- .../Plugin/Epay/AddPayloadSignPluginTest.php | 32 ++++++++++++++ .../Plugin/Epay/VerifySignaturePluginTest.php | 42 +++++++++++++++++++ tests/Provider/EpayTest.php | 6 --- web/docs/v3/epay/all.md | 39 +++++++++++++++++ web/docs/v3/epay/callback.md | 42 +++++++++++++++++++ web/docs/v3/epay/pay.md | 31 ++++++++++++++ web/docs/v3/epay/query.md | 34 +++++++++++++++ web/docs/v3/epay/refund.md | 25 +++++++++++ web/docs/v3/epay/response.md | 17 ++++++++ 11 files changed, 269 insertions(+), 25 deletions(-) create mode 100644 web/docs/v3/epay/all.md create mode 100644 web/docs/v3/epay/callback.md create mode 100644 web/docs/v3/epay/pay.md create mode 100644 web/docs/v3/epay/query.md create mode 100644 web/docs/v3/epay/refund.md create mode 100644 web/docs/v3/epay/response.md diff --git a/src/Plugin/Epay/AddPayloadSignPlugin.php b/src/Plugin/Epay/AddPayloadSignPlugin.php index cc6954a37..fb1fb8e62 100644 --- a/src/Plugin/Epay/AddPayloadSignPlugin.php +++ b/src/Plugin/Epay/AddPayloadSignPlugin.php @@ -7,6 +7,7 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; use Yansongda\Artful\Exception\ContainerException; +use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\InvalidParamsException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; @@ -21,7 +22,7 @@ class AddPayloadSignPlugin implements PluginInterface /** * @throws ServiceNotFoundException * @throws InvalidParamsException - * @throws ContainerException + * @throws ContainerException|InvalidConfigException */ public function assembly(Rocket $rocket, Closure $next): Rocket { @@ -61,7 +62,7 @@ protected function getPrivateKey(array $config): string $privateCertPath = $config['mch_secret_cert_path'] ?? ''; if (!$privateCertPath) { - throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); + throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, '参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); } return file_get_contents($privateCertPath); diff --git a/src/Plugin/Epay/VerifySignaturePlugin.php b/src/Plugin/Epay/VerifySignaturePlugin.php index c25ad4190..36c6cf5fd 100644 --- a/src/Plugin/Epay/VerifySignaturePlugin.php +++ b/src/Plugin/Epay/VerifySignaturePlugin.php @@ -7,6 +7,7 @@ use Closure; use Yansongda\Artful\Contract\PluginInterface; use Yansongda\Artful\Exception\ContainerException; +use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; @@ -23,7 +24,7 @@ class VerifySignaturePlugin implements PluginInterface { /** * @throws ServiceNotFoundException - * @throws ContainerException|InvalidSignException + * @throws ContainerException|InvalidConfigException|InvalidSignException */ public function assembly(Rocket $rocket, Closure $next): Rocket { @@ -45,7 +46,7 @@ public function assembly(Rocket $rocket, Closure $next): Rocket } /** - * @throws InvalidSignException + * @throws InvalidConfigException|InvalidSignException */ protected function verifySign(array $config, string $body): void { @@ -58,7 +59,7 @@ protected function verifySign(array $config, string $body): void $publicCert = $config['epay_public_cert_path'] ?? null; if (empty($publicCert)) { - throw new InvalidSignException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 'Missing Epay Config -- [epay_public_cert_path]'); + throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [epay_public_cert_path]'); } $result = 1 === openssl_verify( $signatureData['data'], @@ -70,20 +71,6 @@ protected function verifySign(array $config, string $body): void } } - protected function query(string $body): array - { - $result = []; - foreach (explode('&', $body) as $item) { - $pos = strpos($item, '='); - if (!$pos) { - continue; - } - $result[substr($item, 0, $pos)] = substr($item, $pos + 1); - } - - return $result; - } - private function getSignatureData(string $body): array { if (Str::contains($body, '&-&')) { diff --git a/tests/Plugin/Epay/AddPayloadSignPluginTest.php b/tests/Plugin/Epay/AddPayloadSignPluginTest.php index 823827dc9..53b7b283e 100644 --- a/tests/Plugin/Epay/AddPayloadSignPluginTest.php +++ b/tests/Plugin/Epay/AddPayloadSignPluginTest.php @@ -2,10 +2,16 @@ namespace Yansongda\Pay\Tests\Plugin\Epay; +use Yansongda\Artful\Contract\ConfigInterface; +use Yansongda\Artful\Exception\InvalidConfigException; +use Yansongda\Artful\Exception\InvalidParamsException; use Yansongda\Artful\Rocket; +use Yansongda\Pay\Exception\Exception; +use Yansongda\Pay\Pay; use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; use Yansongda\Pay\Tests\TestCase; use Yansongda\Supports\Collection; +use Yansongda\Supports\Config; class AddPayloadSignPluginTest extends TestCase { @@ -28,4 +34,30 @@ public function testSignNormal() $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($sign, $result->getPayload()->get('sign')); } + + public function testEmptyPayload() + { + self::expectException(InvalidParamsException::class); + self::expectExceptionCode(Exception::PARAMS_NECESSARY_PARAMS_MISSING); + self::expectExceptionMessage('参数异常: epay支付必要参数缺失。可能插件用错顺序,应该先使用 `业务插件`'); + $rocket = new Rocket(); + $rocket->setParams([])->setPayload(new Collection()); + + $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + } + + public function testMissMchSecretCertPath() + { + $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'元仓充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; + $sign = "bDVcd91eQNVMyf7on1/YewqyNzBdorHy3/BA7L89CKuyDZtf/FId/GbGTBbn1QzGsLB8Vcultv1BxFrMGJUrEF73HO4rHtkAXKu9hna6KmtDCXRVNJw6fjuU9epXdJqE1RSB8f0j5HKDOxD2LBSZB1ZAbmH5v2WMCvx83cYpjOQ="; + $rocket = new Rocket(); + $rocket->setParams([])->setPayload(new Collection($payload)); + + Pay::set(ConfigInterface::class, new Config()); + self::expectException(InvalidConfigException::class); + self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); + self::expectExceptionMessage('参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($sign, $result->getPayload()->get('sign')); + } } diff --git a/tests/Plugin/Epay/VerifySignaturePluginTest.php b/tests/Plugin/Epay/VerifySignaturePluginTest.php index 513414860..d55fd3fcf 100644 --- a/tests/Plugin/Epay/VerifySignaturePluginTest.php +++ b/tests/Plugin/Epay/VerifySignaturePluginTest.php @@ -3,10 +3,15 @@ namespace Yansongda\Pay\Tests\Plugin\Epay; use GuzzleHttp\Psr7\Response; +use Yansongda\Artful\Contract\ConfigInterface; +use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Rocket; +use Yansongda\Pay\Exception\Exception; use Yansongda\Pay\Exception\InvalidSignException; +use Yansongda\Pay\Pay; use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; use Yansongda\Pay\Tests\TestCase; +use Yansongda\Supports\Config; class VerifySignaturePluginTest extends TestCase { @@ -29,9 +34,31 @@ public function testSignNormal() self::assertSame($rocket, $result); } + public function testMulconnectorSign() + { + $body = 'partnerId=6a13eab71c4f4b0aa4757eda6fc59710&field1=&field2=&field3=&respBizDate=20240618&respCode=000000&respMsg=交易成功&totalRows=17&pageSize=20¤tPage=1&totalPages=1&hasNext=0&hasPrevious=0&signType=RSA&sign=alEzu2R3ZquH9ff0bI4b9Cl4MDDnEM3vPtMGwfwAVYJuYuecCFCf36glEiHu+KHxG/kzyRnDpakmeSPoGfs2GLsSxzxU6p3pNdBesbkZvU8j2WVUdwq1DZ6Z6SDqS1ZEMiRBGymePOGbBows+/DY8RrTplx4j0TvKqCUrfJun4o=&-&transDate=20240617&transTime=20240617151229&orderNo=20240617151229401202589&outTradeNo=RK-YC202406170004&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240617&transTime=20240617150911&orderNo=20240617150911400947909&outTradeNo=YC202406170004&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=元仓充值&extfld3=20240617150917&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613164332&orderNo=20240613164332401166829&outTradeNo=RC240613164222066849&amount=0.03&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=YC002充值&extfld3=20240613164337&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613164134&orderNo=20240613164134400800219&outTradeNo=RC240613164110030316&amount=0.02&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=YC002充值&extfld3=20240613164139&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613161540&orderNo=20240613161540418461859&outTradeNo=RC240613161530097918&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=YC002充值&extfld3=20240613161551&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613153839&orderNo=20240613153839416172119&outTradeNo=RC240613153823029572&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=YC002充值&extfld3=20240613153847&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613145133&orderNo=20240613145133412731519&outTradeNo=YC202406132022&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=test充值&extfld3=20240613145138&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613144454&orderNo=20240613144454412182359&outTradeNo=TK-YC202406132021&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613144333&orderNo=20240613144333411961589&outTradeNo=YC202406132021&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=test充值&extfld3=20240613144349&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613144016&orderNo=20240613144016411383399&outTradeNo=TK-RC240613142104066624&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613144004&orderNo=20240613144004411355039&outTradeNo=TK-RC240613142846088651&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613143945&orderNo=20240613143945411253709&outTradeNo=TK-RC240613143227055276&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613143317&orderNo=20240613143317410648749&outTradeNo=RC240613143227055276&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=YC002充值&extfld3=20240613143338&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613142859&orderNo=20240613142859410207589&outTradeNo=RC240613142846088651&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=YC002充值&extfld3=20240613142905&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613142143&orderNo=20240613142143409559519&outTradeNo=RC240613142104066624&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=YC002充值&extfld3=20240613142149&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613105059&orderNo=20240613105059400587329&outTradeNo=RT-YC202406130102&amount=0.01&orderStatus=1&orderType=2&checkStatus=&tradeType=2&extfld1=0.0|0.0&extfld2=&extfld3=&deviceNo=1234567890&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8&-&transDate=20240613&transTime=20240613101235&orderNo=20240613101235476739849&outTradeNo=YC202406130102&amount=0.01&orderStatus=1&orderType=1&checkStatus=&tradeType=2&extfld1=0.0|0.01&extfld2=元仓充值&extfld3=20240613101243&deviceNo=&operatorId=&payId=oUpF8uLdyMJiT0t792_LFbuv1Lz8'; + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + + + public function testEmptySign() + { + self::expectException(InvalidSignException::class); + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2'; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + public function testSignWrong() { self::expectException(InvalidSignException::class); + self::expectExceptionCode(Exception::SIGN_ERROR); $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); @@ -39,4 +66,19 @@ public function testSignWrong() $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); } + + public function testMissingEpayPublicCertPath() + { + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; + + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + Pay::set(ConfigInterface::class, new Config()); + self::expectException(InvalidConfigException::class); + self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); + self::expectExceptionMessage('Missing Epay Config -- [epay_public_cert_path]'); + + $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); + self::assertSame($rocket, $result); + } + } diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index d6ea5d65d..ecaa645e7 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -22,11 +22,6 @@ use Yansongda\Pay\Tests\Stubs\Plugin\FooPluginStub; use Yansongda\Pay\Tests\TestCase; -/** - * @internal - * - * @coversNothing - */ class EpayTest extends TestCase { public function testShortcutNotFound() @@ -63,7 +58,6 @@ public function testScan() 'proInfo' => '元仓充值', 'backUrl' => 'http://47.242.204.179:8000/api/user/recharge/ePayNotice', ]); - self::assertArrayHasKey('payUrl', $result->all()); } diff --git a/web/docs/v3/epay/all.md b/web/docs/v3/epay/all.md new file mode 100644 index 000000000..b7c8fa424 --- /dev/null +++ b/web/docs/v3/epay/all.md @@ -0,0 +1,39 @@ +# e融支付更多方便的插件 + +得益于 yansongda/pay 的基础架构和良好的插件机制, +您可以自有的使用任何内置插件和自定义插件调用支付宝的任何 API。 + +诸如签名、API调用、解密、验签、解包等基础插件已经内置在 Pay 中, +您可以使用 `Pay::epay()->mergeCommonPlugins(array $plugins)` 来获取调用 API 所必须的常用插件 + +首先,查找你想使用的插件,然后 + +```php +Pay::config($config); + +$params = [ + 'outTradeNo' => '1514027114', +]; + +$allPlugins = Pay::epay()->mergeCommonPlugins([QueryPlugin::class]); + +$result = Pay::epay()->pay($allPlugins, $params); +``` + +关于插件的详细介绍,如果您感兴趣,可以参考 [yansongda/artful](https://artful.yansongda.cn/) + +## 支付 + +### 扫码支付(聚合支付, 支持支付宝、微信、银联、e融支付) + +- 交易预创建 + + `\Yansongda\Pay\Plugin\Epay\Pay\Scan\PrepayPlugin` + +- 交易退款接口 + + `\Yansongda\Pay\Plugin\Epay\Pay\Scan\RefundPlugin` + +- 交易/退款结果查询 + + `\Yansongda\Pay\Plugin\Epay\Pay\Scan\QueryPlugin` diff --git a/web/docs/v3/epay/callback.md b/web/docs/v3/epay/callback.md new file mode 100644 index 000000000..d3e827c00 --- /dev/null +++ b/web/docs/v3/epay/callback.md @@ -0,0 +1,42 @@ +# 接收e融支付回调 + +| 方法名 | 参数 | 返回值 | +|:--------:|:------------------------------:|:----------:| +| callback | 无/array/ServerRequestInterface | Collection | + +使用的加密方式为支付宝官方推荐的 **RSA2**,目前只支持这一种加密方式,且没有支持其他加密方式的计划。 + +## 例子 + +```php +Pay::config($this->config); + +// 是的,你没有看错,就是这么简单! +$result = Pay::epay()->callback(); +``` + +## 参数 + +### 第一个参数 + +#### `null` + +如果您没有传参,或传 `null` 则 `yansongda/pay` 会自动识别支付宝的回调请求并处理,通过 `Collection` 实例返回支付宝的处理参数 + +:::warning +建议仅在 php-fpm 下使用,swoole 方式请使用 `ServerRequestInterface` 参数传递方式 +::: + +#### `ServerRequestInterface` + +推荐在 swoole 环境下传递此参数,传递此参数后, yansongda/pay 会自动进行后续处理 + +#### `array` + +也可以自行解析请求参数,传递一个 array 会自动进行后续处理 + +### 第二个参数 + +第二个参数主要是传递相关自定义变量的,类似于 `web()` 中的 `_config` / `_method` 等参数。 + +例如,如果你想在回调的时候使用非默认配置,则可以 `Pay::alipay()->callback(null, ['_config' => 'yansongda'])` 切换为 `yansongda` 这个租户的配置信息。 diff --git a/web/docs/v3/epay/pay.md b/web/docs/v3/epay/pay.md new file mode 100644 index 000000000..333753163 --- /dev/null +++ b/web/docs/v3/epay/pay.md @@ -0,0 +1,31 @@ +# e融支付 + +e融支付目前直接内置支持以下快捷方式支付方法,对应的支付 method 如下: + +| method | 说明 | 参数 | 返回值 | +|:--------:|:------:|:------------:|:----------:| +| scan | 扫码支付 | array $order | Collection | + +更多接口调用请参考后续文档 + +## 网页支付 + +### 例子 + +```php +Pay::config($this->config); + +$result = Pay::epay()->scan([ + 'outTradeNo' => 'YC202406170003', + 'totalFee' => 0.01, + 'proInfo' => '充值' + ]); + +//可通过$result->payUrl参数生成二维码支付链接 +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了** + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考支付文档 diff --git a/web/docs/v3/epay/query.md b/web/docs/v3/epay/query.md new file mode 100644 index 000000000..b9ac40ca4 --- /dev/null +++ b/web/docs/v3/epay/query.md @@ -0,0 +1,34 @@ +# e融支付查询订单(退款订单 or 交易支付订单) + +| 方法名 | 参数 | 返回值 | +|:-----:|:------------:|:----------:| +| query | array $order | Collection | + +## 查询交易支付订单 + +```php +Pay::config($this->config); + +//查询交易支付订单 +$order = [ + 'outTradeNo' => '1514027114', +]; +$result = Pay::epay()->query($order); +``` + +## 查询退款订单 + +```php +Pay::config($this->config); + +//查询退款单号查询退款订单 +$order = [ + 'outTradeNo' => 'RK-1514027114', +]; + +$result = Pay::epay()->query($order); +``` + +### 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考文档。 diff --git a/web/docs/v3/epay/refund.md b/web/docs/v3/epay/refund.md new file mode 100644 index 000000000..53e854a92 --- /dev/null +++ b/web/docs/v3/epay/refund.md @@ -0,0 +1,25 @@ +# 支付宝退款 + +| 方法名 | 参数 | 返回值 | +|:------:|:------------:|:----------:| +| refund | array $order | Collection | + +:::warning 注意 +更多功能请查看源码,文档里仅仅列举了一些常用的功能。 +::: + +## 普通退款操作 + +```php +Pay::config($this->config); + +$result = Pay::epay()->refund([ + 'outTradeNo' => 'YC202406170004', + 'refundAmt' => 0.01, + 'outRefundNo' => 'TK-YC202406170004', +]); +``` + +### 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考支付文档。 diff --git a/web/docs/v3/epay/response.md b/web/docs/v3/epay/response.md new file mode 100644 index 000000000..20c534193 --- /dev/null +++ b/web/docs/v3/epay/response.md @@ -0,0 +1,17 @@ +# e融支付确认回调 + +| 方法名 | 参数 | 返回值 | +|:-------:|:---:|:--------:| +| success | 无 | Response | + +## 例子 + +```php +Pay::config($config); + +return Pay::epay()->success(); +``` + +## 配置参数 + +无 From c63573a8c3d2d954cebd4e7c42360d6d7a50f98e Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 15:34:41 +0800 Subject: [PATCH 11/17] =?UTF-8?q?test:=20=E5=8E=BB=E9=99=A4ip=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Provider/EpayTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index ecaa645e7..d0b43997e 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -56,7 +56,6 @@ public function testScan() 'outTradeNo' => 'YC202406170003', 'totalFee' => 0.01, 'proInfo' => '元仓充值', - 'backUrl' => 'http://47.242.204.179:8000/api/user/recharge/ePayNotice', ]); self::assertArrayHasKey('payUrl', $result->all()); } From dbefede10a84d644b5e511ddfe375634d30b5101 Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 15:51:51 +0800 Subject: [PATCH 12/17] =?UTF-8?q?test:=20=E5=8E=BB=E9=99=A4=E6=95=8F?= =?UTF-8?q?=E6=84=9F=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Plugin/Epay/AddPayloadSignPluginTest.php | 8 ++++---- tests/Provider/EpayTest.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Plugin/Epay/AddPayloadSignPluginTest.php b/tests/Plugin/Epay/AddPayloadSignPluginTest.php index 53b7b283e..020c81bbc 100644 --- a/tests/Plugin/Epay/AddPayloadSignPluginTest.php +++ b/tests/Plugin/Epay/AddPayloadSignPluginTest.php @@ -26,8 +26,8 @@ protected function setUp(): void public function testSignNormal() { - $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'元仓充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; - $sign = "bDVcd91eQNVMyf7on1/YewqyNzBdorHy3/BA7L89CKuyDZtf/FId/GbGTBbn1QzGsLB8Vcultv1BxFrMGJUrEF73HO4rHtkAXKu9hna6KmtDCXRVNJw6fjuU9epXdJqE1RSB8f0j5HKDOxD2LBSZB1ZAbmH5v2WMCvx83cYpjOQ="; + $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; + $sign = "Bho3LZvuv6wrQUAk6EP5lpGTCf5nDA1KDQwJy5Cog6m9S3UMVqpn0AC8+rrv5va63z5zAC6aQ7qrVH1OQ3hCeEUhhGix5HRUNgs2lzCkpywQnNsjeuapAAmfzVnDfBncPv9HuZSfdGxCOPqlkaSxonSXbB5ZpUfXbH3QjQo2F2w="; $rocket = new Rocket(); $rocket->setParams([])->setPayload(new Collection($payload)); @@ -48,8 +48,8 @@ public function testEmptyPayload() public function testMissMchSecretCertPath() { - $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'元仓充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; - $sign = "bDVcd91eQNVMyf7on1/YewqyNzBdorHy3/BA7L89CKuyDZtf/FId/GbGTBbn1QzGsLB8Vcultv1BxFrMGJUrEF73HO4rHtkAXKu9hna6KmtDCXRVNJw6fjuU9epXdJqE1RSB8f0j5HKDOxD2LBSZB1ZAbmH5v2WMCvx83cYpjOQ="; + $payload = ['outTradeNo'=>'YC202406170003','totalFee'=>0.01,'proInfo'=>'充值','backUrl'=>'http:\/\/127.0.0.1:8000\/epay\/return','createData'=>'20240618','createTime'=>'022522','bizDate'=>'20240618','msgId'=>'16253083-49c4-4142-8c56-997accf3d667','svrCode'=>'','partnerId'=>'6a13eab71c4f4b0aa4757eda6fc59710','channelNo'=>'m','publicKeyCode'=>'00','version'=>'v1.0.0','charset'=>'utf-8','service'=>'atPay']; + $sign = "Bho3LZvuv6wrQUAk6EP5lpGTCf5nDA1KDQwJy5Cog6m9S3UMVqpn0AC8+rrv5va63z5zAC6aQ7qrVH1OQ3hCeEUhhGix5HRUNgs2lzCkpywQnNsjeuapAAmfzVnDfBncPv9HuZSfdGxCOPqlkaSxonSXbB5ZpUfXbH3QjQo2F2w="; $rocket = new Rocket(); $rocket->setParams([])->setPayload(new Collection($payload)); diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index d0b43997e..a278f19d6 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -55,7 +55,7 @@ public function testScan() $result = Pay::epay()->scan([ 'outTradeNo' => 'YC202406170003', 'totalFee' => 0.01, - 'proInfo' => '元仓充值', + 'proInfo' => '充值', ]); self::assertArrayHasKey('payUrl', $result->all()); } From aeb0b04d3512f2d90171191e193fcf8098d4cb0c Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Tue, 18 Jun 2024 17:36:50 +0800 Subject: [PATCH 13/17] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Epay/AddRadarPlugin.php | 5 - tests/Plugin/Epay/AddRadarPluginTest.php | 7 ++ tests/Plugin/Epay/CallbackPluginTest.php | 105 ++++++++++++++++++ tests/Plugin/Epay/ResponsePluginTest.php | 1 + .../Plugin/Epay/VerifySignaturePluginTest.php | 2 + tests/Provider/EpayTest.php | 52 ++++++++- 6 files changed, 166 insertions(+), 6 deletions(-) diff --git a/src/Plugin/Epay/AddRadarPlugin.php b/src/Plugin/Epay/AddRadarPlugin.php index fa98ad02a..99092fc30 100644 --- a/src/Plugin/Epay/AddRadarPlugin.php +++ b/src/Plugin/Epay/AddRadarPlugin.php @@ -42,11 +42,6 @@ public function assembly(Rocket $rocket, Closure $next): Rocket return $next($rocket); } - protected function getMethod(array $params): string - { - return strtoupper($params['_method'] ?? 'POST'); - } - protected function getHeaders(): array { return [ diff --git a/tests/Plugin/Epay/AddRadarPluginTest.php b/tests/Plugin/Epay/AddRadarPluginTest.php index 07cae3fc0..58deb7255 100644 --- a/tests/Plugin/Epay/AddRadarPluginTest.php +++ b/tests/Plugin/Epay/AddRadarPluginTest.php @@ -29,5 +29,12 @@ public function testRadarPostNormal() self::assertEquals('https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', (string) $result->getRadar()->getUri()); self::stringContains('name=yansongda', (string) $result->getRadar()->getBody()); self::assertEquals('POST', $result->getRadar()->getMethod()); + + //是否存在Content-Type和User-Agent + self::assertArrayHasKey('Content-Type', $result->getRadar()->getHeaders()); + self::assertArrayHasKey('User-Agent', $result->getRadar()->getHeaders()); + //验证值 + self::assertEquals('text/html', $result->getRadar()->getHeader('Content-Type')[0]); + self::assertEquals('yansongda/pay-v3', $result->getRadar()->getHeader('User-Agent')[0]); } } diff --git a/tests/Plugin/Epay/CallbackPluginTest.php b/tests/Plugin/Epay/CallbackPluginTest.php index cff4fc3e1..5e42e9333 100644 --- a/tests/Plugin/Epay/CallbackPluginTest.php +++ b/tests/Plugin/Epay/CallbackPluginTest.php @@ -3,10 +3,17 @@ namespace Yansongda\Pay\Tests\Plugin\Epay; use GuzzleHttp\Psr7\ServerRequest; +use Yansongda\Artful\Contract\ConfigInterface; +use Yansongda\Artful\Exception\InvalidConfigException; +use Yansongda\Artful\Exception\InvalidParamsException; +use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Rocket; +use Yansongda\Pay\Exception\Exception; +use Yansongda\Pay\Pay; use Yansongda\Pay\Plugin\Epay\CallbackPlugin; use Yansongda\Pay\Tests\TestCase; use Yansongda\Supports\Collection; +use Yansongda\Supports\Config; class CallbackPluginTest extends TestCase { @@ -44,4 +51,102 @@ public function testNormal() self::assertNotEmpty($result->getPayload()->all()); } + public function testErrorSign() + { + self::expectException(InvalidConfigException::class); + self::expectExceptionCode(Exception::SIGN_ERROR); + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030315', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $request = new ServerRequest('POST', 'http://localhost'); + $request = $request->withParsedBody($payload); + + $rocket = new Rocket(); + $rocket->setParams(['request'=>Collection::wrap($request->getParsedBody())]); + + $result = $this->plugin->assembly($rocket, function ($rocket) {return $rocket;}); + + self::assertNotEmpty($result->getPayload()->all()); + } + + public function testEmptySign() + { + self::expectException(InvalidResponseException::class); + self::expectExceptionCode(Exception::SIGN_ERROR); + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> '' + ]; + $request = new ServerRequest('POST', 'http://localhost'); + $request = $request->withParsedBody($payload); + + $rocket = new Rocket(); + $rocket->setParams(['request'=>Collection::wrap($request->getParsedBody())]); + + $result = $this->plugin->assembly($rocket, function ($rocket) {return $rocket;}); + + self::assertNotEmpty($result->getPayload()->all()); + } + + public function testErrorRequestType() + { + self::expectException(InvalidParamsException::class); + self::expectExceptionCode(Exception::PARAMS_CALLBACK_REQUEST_INVALID); + $payload = [ + ]; + $request = new ServerRequest('POST', 'http://localhost'); + $request = $request->withParsedBody($payload); + + $rocket = new Rocket(); + $rocket->setParams(['request'=>$request->getParsedBody()]); + + $result = $this->plugin->assembly($rocket, function ($rocket) {return $rocket;}); + + self::assertNotEmpty($result->getPayload()->all()); + } + + public function testMissingEpayPublicCertPath() + { + self::expectException(InvalidConfigException::class); + self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); + Pay::set(ConfigInterface::class, new Config()); + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $request = new ServerRequest('POST', 'http://localhost'); + $request = $request->withParsedBody($payload); + + $rocket = new Rocket(); + $rocket->setParams(['request'=>Collection::wrap($request->getParsedBody())]); + + $result = $this->plugin->assembly($rocket, function ($rocket) {return $rocket;}); + + self::assertNotEmpty($result->getPayload()->all()); + } } diff --git a/tests/Plugin/Epay/ResponsePluginTest.php b/tests/Plugin/Epay/ResponsePluginTest.php index c7dc28705..28edcc8a9 100644 --- a/tests/Plugin/Epay/ResponsePluginTest.php +++ b/tests/Plugin/Epay/ResponsePluginTest.php @@ -45,6 +45,7 @@ public function testHttpWrong() { self::expectException(InvalidResponseException::class); self::expectExceptionCode(Exception::RESPONSE_ERROR); + self::expectExceptionMessage('epay返回状态码异常,请检查参数是否错误'); $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)); diff --git a/tests/Plugin/Epay/VerifySignaturePluginTest.php b/tests/Plugin/Epay/VerifySignaturePluginTest.php index d55fd3fcf..e54535e8e 100644 --- a/tests/Plugin/Epay/VerifySignaturePluginTest.php +++ b/tests/Plugin/Epay/VerifySignaturePluginTest.php @@ -47,6 +47,8 @@ public function testMulconnectorSign() public function testEmptySign() { self::expectException(InvalidSignException::class); + self::expectExceptionCode(Exception::RESPONSE_MISSING_NECESSARY_PARAMS); + $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2'; $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); diff --git a/tests/Provider/EpayTest.php b/tests/Provider/EpayTest.php index a278f19d6..a4a4d7dc5 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/EpayTest.php @@ -6,6 +6,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\ServerRequest; use Mockery; use Psr\Http\Message\ResponseInterface; use Yansongda\Artful\Contract\HttpClientInterface; @@ -125,7 +126,7 @@ public function testClose() { $this->expectException(InvalidParamsException::class); - Pay::epay()->cancel([ + Pay::epay()->close([ 'outTradeNo' => 'YC202406170003', ]); } @@ -153,6 +154,55 @@ public function testCallback() } + public function testCallbackByFromGlobals() + { + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $_POST = array_merge($_POST, $payload); + $result = Pay::epay()->callback(); + self::assertNotEmpty($result->all()); + self::assertArrayHasKey('outTradeNo', $result->all()); + self::assertArrayHasKey('orderNo', $result->all()); + self::assertArrayHasKey('orderStatus', $result->all()); + self::assertArrayHasKey('field3', $result->all()); + + } + + public function testCallbackByServerRequest() + { + $payload = [ + 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', + 'orderStatus'=> '1', + 'totalFee'=> '0.02', + 'outTradeNo'=> 'RC240613164110030316', + 'orderNo'=> '20240613164114400729509', + 'field1'=> '2', + 'field2'=> '', + 'field3'=> '20240613164139|20240613164134400800219', + 'signType'=> 'RSA', + 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' + ]; + $serverRequest = new ServerRequest('POST', 'http://localhost', [], null); + $serverRequest = $serverRequest->withParsedBody($payload); + $result = Pay::epay()->callback($serverRequest); + self::assertNotEmpty($result->all()); + self::assertArrayHasKey('outTradeNo', $result->all()); + self::assertArrayHasKey('orderNo', $result->all()); + self::assertArrayHasKey('orderStatus', $result->all()); + self::assertArrayHasKey('field3', $result->all()); + + } + public function testSuccess() { $result = Pay::epay()->success(); From 289f19783db02b44e72a26dd0e5c2e71a39afb42 Mon Sep 17 00:00:00 2001 From: yansongda Date: Fri, 21 Jun 2024 23:54:08 +0800 Subject: [PATCH 14/17] update --- CHANGELOG.md | 6 ++ README.md | 44 +++++++------- src/Exception/Exception.php | 2 +- src/Functions.php | 33 ++++++++++- src/Pay.php | 8 +-- .../{Epay => Jsb}/AddPayloadSignPlugin.php | 37 +++++------- src/Plugin/{Epay => Jsb}/AddRadarPlugin.php | 15 +++-- src/Plugin/{Epay => Jsb}/CallbackPlugin.php | 47 ++++----------- .../Pay/Scan/PayPlugin.php} | 21 +++---- .../{Epay => Jsb}/Pay/Scan/QueryPlugin.php | 6 +- .../{Epay => Jsb}/Pay/Scan/RefundPlugin.php | 6 +- src/Plugin/{Epay => Jsb}/ResponsePlugin.php | 13 +++-- src/Plugin/{Epay => Jsb}/StartPlugin.php | 17 +++--- .../{Epay => Jsb}/VerifySignaturePlugin.php | 45 ++++---------- src/Provider/{Epay.php => Jsb.php} | 47 ++++++++++----- ...iceProvider.php => JsbServiceProvider.php} | 10 ++-- src/Shortcut/{Epay => Jsb}/QueryShortcut.php | 14 ++--- src/Shortcut/{Epay => Jsb}/RefundShortcut.php | 14 ++--- src/Shortcut/{Epay => Jsb}/ScanShortcut.php | 16 ++--- tests/FunctionTest.php | 8 +-- tests/PayTest.php | 6 +- .../AddPayloadSignPluginTest.php | 10 ++-- .../{Epay => Jsb}/AddRadarPluginTest.php | 4 +- .../{Epay => Jsb}/CallbackPluginTest.php | 13 +++-- .../Pay/Scan/PayPluginTest.php} | 10 ++-- .../Pay/Scan/QueryPluginTest.php | 4 +- .../Pay/Scan/RefundPluginTest.php | 4 +- .../{Epay => Jsb}/ResponsePluginTest.php | 14 ++--- .../Plugin/{Epay => Jsb}/StartPluginTest.php | 4 +- .../VerifySignaturePluginTest.php | 10 ++-- tests/Provider/{EpayTest.php => JsbTest.php} | 36 ++++++------ .../{Epay => Jsb}/QueryShortcutTest.php | 16 ++--- .../{Epay => Jsb}/RefundShortcutTest.php | 16 ++--- .../{Epay => Jsb}/ScanShortcutTest.php | 18 +++--- tests/TestCase.php | 4 +- web/.vitepress/sidebar/v3.js | 13 +++++ web/docs/v3/{epay => jsb}/all.md | 12 ++-- web/docs/v3/{epay => jsb}/callback.md | 6 +- web/docs/v3/{epay => jsb}/pay.md | 8 +-- web/docs/v3/{epay => jsb}/query.md | 6 +- web/docs/v3/{epay => jsb}/refund.md | 4 +- web/docs/v3/{epay => jsb}/response.md | 4 +- web/docs/v3/quick-start/init.md | 20 +++++++ web/docs/v3/quick-start/jsb.md | 58 +++++++++++++++++++ 44 files changed, 401 insertions(+), 308 deletions(-) rename src/Plugin/{Epay => Jsb}/AddPayloadSignPlugin.php (58%) rename src/Plugin/{Epay => Jsb}/AddRadarPlugin.php (80%) rename src/Plugin/{Epay => Jsb}/CallbackPlugin.php (54%) rename src/Plugin/{Epay/Pay/Scan/PrepayPlugin.php => Jsb/Pay/Scan/PayPlugin.php} (54%) rename src/Plugin/{Epay => Jsb}/Pay/Scan/QueryPlugin.php (64%) rename src/Plugin/{Epay => Jsb}/Pay/Scan/RefundPlugin.php (64%) rename src/Plugin/{Epay => Jsb}/ResponsePlugin.php (71%) rename src/Plugin/{Epay => Jsb}/StartPlugin.php (73%) rename src/Plugin/{Epay => Jsb}/VerifySignaturePlugin.php (56%) rename src/Provider/{Epay.php => Jsb.php} (71%) rename src/Service/{EpayServiceProvider.php => JsbServiceProvider.php} (61%) rename src/Shortcut/{Epay => Jsb}/QueryShortcut.php (61%) rename src/Shortcut/{Epay => Jsb}/RefundShortcut.php (61%) rename src/Shortcut/{Epay => Jsb}/ScanShortcut.php (57%) rename tests/Plugin/{Epay => Jsb}/AddPayloadSignPluginTest.php (85%) rename tests/Plugin/{Epay => Jsb}/AddRadarPluginTest.php (93%) rename tests/Plugin/{Epay => Jsb}/CallbackPluginTest.php (93%) rename tests/Plugin/{Epay/Pay/Scan/PrepayPluginTest.php => Jsb/Pay/Scan/PayPluginTest.php} (64%) rename tests/Plugin/{Epay => Jsb}/Pay/Scan/QueryPluginTest.php (84%) rename tests/Plugin/{Epay => Jsb}/Pay/Scan/RefundPluginTest.php (84%) rename tests/Plugin/{Epay => Jsb}/ResponsePluginTest.php (86%) rename tests/Plugin/{Epay => Jsb}/StartPluginTest.php (91%) rename tests/Plugin/{Epay => Jsb}/VerifySignaturePluginTest.php (96%) rename tests/Provider/{EpayTest.php => JsbTest.php} (91%) rename tests/Shortcut/{Epay => Jsb}/QueryShortcutTest.php (58%) rename tests/Shortcut/{Epay => Jsb}/RefundShortcutTest.php (58%) rename tests/Shortcut/{Epay => Jsb}/ScanShortcutTest.php (56%) rename web/docs/v3/{epay => jsb}/all.md (69%) rename web/docs/v3/{epay => jsb}/callback.md (85%) rename web/docs/v3/{epay => jsb}/pay.md (79%) rename web/docs/v3/{epay => jsb}/query.md (79%) rename web/docs/v3/{epay => jsb}/refund.md (89%) rename web/docs/v3/{epay => jsb}/response.md (72%) create mode 100644 web/docs/v3/quick-start/jsb.md diff --git a/CHANGELOG.md b/CHANGELOG.md index a068e9ecc..50d2bdfec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.7.7 + +### added + +- feat: 新增江苏银行e融支付(#1002) + ## v3.7.6 ### fixed diff --git a/README.md b/README.md index 13b501a13..026c62666 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ yansongda/pay 100% 兼容 支付宝/微信/银联 所有功能(包括服务商 - 扫码支付 - ... - -### e融支付(江苏银行) +### 江苏银行(e融支付) - 聚合扫码支付(微信,支付宝,银联,e融) - ... @@ -284,7 +284,7 @@ class WechatController } ``` -### e融支付(epay) +### 江苏银行(e融支付) ```php [ + 'jsb' => [ 'default' => [ - // 服务代码 - 'svr_code' => '', - // 必填-合作商ID - 'partner_id' => '', - // 必填-公私钥对编号 - 'public_key_code' => '00', - // 必填-商户私钥(加密签名) - 'mch_secret_cert_path' => '', - // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) - 'mch_public_cert_path' => '', - // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) - 'epay_public_cert_path' => '', - //支付通知地址 - 'notify_url' => '', - // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:测试环境 - 'mode' => Pay::MODE_NORMAL, + // 服务代码 + 'svr_code' => '', + // 必填-合作商ID + 'partner_id' => '', + // 必填-公私钥对编号 + 'public_key_code' => '00', + // 必填-商户私钥(加密签名) + 'mch_secret_cert_path' => '', + // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) + 'mch_public_cert_path' => '', + // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) + 'jsb_public_cert_path' => '', + //支付通知地址 + 'notify_url' => '', + // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:测试环境 + 'mode' => Pay::MODE_NORMAL, ] ], 'logger' => [ // optional @@ -337,12 +337,12 @@ class EpayController 'totalFee'=> 1, ]; - $pay = Pay::epay($this->config)->scan($order); + $pay = Pay::jsb($this->config)->scan($order); } public function notifyCallback() { - $pay = Pay::epay($this->config); + $pay = Pay::jsb($this->config); try{ $data = $pay->callback(); // 是的,验签就这么简单! @@ -357,7 +357,7 @@ class EpayController ## 代码贡献 -由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 +由于测试及使用环境的限制,本项目中只开发了「支付宝」、「微信支付」、「银联」、「江苏银行」的相关支付网关。 如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_** diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 9c400a44d..e18089035 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -55,7 +55,7 @@ class Exception extends \Exception public const CONFIG_UNIPAY_INVALID = 9403; - public const CONFIG_EPAY_INVALID = 9404; + public const CONFIG_JSB_INVALID = 9404; /** * 关于签名. diff --git a/src/Functions.php b/src/Functions.php index 7c6603d12..51801a88d 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -23,7 +23,7 @@ use Yansongda\Pay\Plugin\Wechat\V3\AddPayloadSignaturePlugin; use Yansongda\Pay\Plugin\Wechat\V3\WechatPublicCertsPlugin; use Yansongda\Pay\Provider\Alipay; -use Yansongda\Pay\Provider\Epay; +use Yansongda\Pay\Provider\Jsb; use Yansongda\Pay\Provider\Unipay; use Yansongda\Pay\Provider\Wechat; use Yansongda\Supports\Collection; @@ -593,12 +593,39 @@ function verify_unipay_sign_qra(array $config, array $destination): void } } -function get_epay_url(array $config, ?Collection $payload): string +function get_jsb_url(array $config, ?Collection $payload): string { $url = get_radar_url($config, $payload) ?? ''; if (str_starts_with($url, 'http')) { return $url; } - return Epay::URL[$config['mode'] ?? Pay::MODE_NORMAL]; + return Jsb::URL[$config['mode'] ?? Pay::MODE_NORMAL]; +} + +/** + * @throws InvalidConfigException + * @throws InvalidSignException + */ +function verify_jsb_sign(array $config, string $content, string $sign): void +{ + if (empty($sign)) { + throw new InvalidSignException(Exception::SIGN_EMPTY, '签名异常: 江苏银行签名为空', func_get_args()); + } + + $publicCert = $config['jsb_public_cert_path'] ?? null; + + if (empty($publicCert)) { + throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [jsb_public_cert_path]'); + } + + $result = 1 === openssl_verify( + $content, + base64_decode($sign), + get_public_cert($publicCert) + ); + + if (!$result) { + throw new InvalidSignException(Exception::SIGN_ERROR, '签名异常: 验证江苏银行签名失败', func_get_args()); + } } diff --git a/src/Pay.php b/src/Pay.php index 87328e6e0..b0d40b13c 100644 --- a/src/Pay.php +++ b/src/Pay.php @@ -10,11 +10,11 @@ use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Pay\Provider\Alipay; -use Yansongda\Pay\Provider\Epay; +use Yansongda\Pay\Provider\Jsb; use Yansongda\Pay\Provider\Unipay; use Yansongda\Pay\Provider\Wechat; use Yansongda\Pay\Service\AlipayServiceProvider; -use Yansongda\Pay\Service\EpayServiceProvider; +use Yansongda\Pay\Service\JsbServiceProvider; use Yansongda\Pay\Service\UnipayServiceProvider; use Yansongda\Pay\Service\WechatServiceProvider; @@ -22,7 +22,7 @@ * @method static Alipay alipay(array $config = [], $container = null) * @method static Wechat wechat(array $config = [], $container = null) * @method static Unipay unipay(array $config = [], $container = null) - * @method static Epay epay(array $config = [], $container = null) + * @method static Jsb jsb(array $config = [], $container = null) */ class Pay { @@ -45,7 +45,7 @@ class Pay AlipayServiceProvider::class, WechatServiceProvider::class, UnipayServiceProvider::class, - EpayServiceProvider::class, + JsbServiceProvider::class, ]; /** diff --git a/src/Plugin/Epay/AddPayloadSignPlugin.php b/src/Plugin/Jsb/AddPayloadSignPlugin.php similarity index 58% rename from src/Plugin/Epay/AddPayloadSignPlugin.php rename to src/Plugin/Jsb/AddPayloadSignPlugin.php index fb1fb8e62..a3c029552 100644 --- a/src/Plugin/Epay/AddPayloadSignPlugin.php +++ b/src/Plugin/Jsb/AddPayloadSignPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -15,35 +15,41 @@ use Yansongda\Pay\Exception\Exception; use Yansongda\Supports\Collection; +use function Yansongda\Pay\get_private_cert; use function Yansongda\Pay\get_provider_config; class AddPayloadSignPlugin implements PluginInterface { /** - * @throws ServiceNotFoundException + * @throws ContainerException + * @throws InvalidConfigException * @throws InvalidParamsException - * @throws ContainerException|InvalidConfigException + * @throws ServiceNotFoundException */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[Epay][AddPayloadSignPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Jsb][AddPayloadSignPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $config = get_provider_config('jsb', $params); $payload = $rocket->getPayload(); if (empty($payload) || $payload->isEmpty()) { - throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: epay支付必要参数缺失。可能插件用错顺序,应该先使用 `业务插件`'); + throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 缺少支付必要参数。可能插件用错顺序,应该先使用 `业务插件`'); + } + + $privateCertPath = $config['mch_secret_cert_path'] ?? ''; + + if (empty($privateCertPath)) { + throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [mch_secret_cert_path]'); } - $pkey = $this->getPrivateKey($config); - $sign = $this->getSignature($pkey, $payload); $rocket->mergePayload([ 'signType' => 'RSA', - 'sign' => $sign, + 'sign' => $this->getSignature(get_private_cert($privateCertPath), $payload), ]); - Logger::info('[Epay][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][AddPayloadSignPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } @@ -56,15 +62,4 @@ protected function getSignature(string $pkey, Collection $payload): string return base64_encode($signature); } - - protected function getPrivateKey(array $config): string - { - $privateCertPath = $config['mch_secret_cert_path'] ?? ''; - - if (!$privateCertPath) { - throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, '参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); - } - - return file_get_contents($privateCertPath); - } } diff --git a/src/Plugin/Epay/AddRadarPlugin.php b/src/Plugin/Jsb/AddRadarPlugin.php similarity index 80% rename from src/Plugin/Epay/AddRadarPlugin.php rename to src/Plugin/Jsb/AddRadarPlugin.php index 99092fc30..870382a1b 100644 --- a/src/Plugin/Epay/AddRadarPlugin.php +++ b/src/Plugin/Jsb/AddRadarPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use GuzzleHttp\Psr7\Request; @@ -13,7 +13,7 @@ use Yansongda\Artful\Rocket; use Yansongda\Supports\Collection; -use function Yansongda\Pay\get_epay_url; +use function Yansongda\Pay\get_jsb_url; use function Yansongda\Pay\get_provider_config; class AddRadarPlugin implements PluginInterface @@ -24,20 +24,20 @@ class AddRadarPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[Epay][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Jsb][AddRadarPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $config = get_provider_config('jsb', $params); $payload = $rocket->getPayload(); $rocket->setRadar(new Request( strtoupper($params['_method'] ?? 'POST'), - get_epay_url($config, $payload), + get_jsb_url($config, $payload), $this->getHeaders(), $this->getBody($payload), )); - Logger::info('[Epay][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][AddRadarPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } @@ -54,9 +54,12 @@ protected function getBody(Collection $payload): string { $sign = $payload->get('sign'); $signType = $payload->get('signType'); + $payload->forget('sign'); $payload->forget('signType'); + $payload = $payload->sortKeys(); + $payload->set('sign', $sign); $payload->set('signType', $signType); diff --git a/src/Plugin/Epay/CallbackPlugin.php b/src/Plugin/Jsb/CallbackPlugin.php similarity index 54% rename from src/Plugin/Epay/CallbackPlugin.php rename to src/Plugin/Jsb/CallbackPlugin.php index 83e1c3632..bb9940dd5 100644 --- a/src/Plugin/Epay/CallbackPlugin.php +++ b/src/Plugin/Jsb/CallbackPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -10,32 +10,33 @@ use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\InvalidConfigException; use Yansongda\Artful\Exception\InvalidParamsException; -use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; +use Yansongda\Pay\Exception\InvalidSignException; use Yansongda\Supports\Collection; use function Yansongda\Pay\get_provider_config; +use function Yansongda\Pay\verify_jsb_sign; class CallbackPlugin implements PluginInterface { /** - * @throws ServiceNotFoundException - * @throws InvalidResponseException - * @throws InvalidConfigException * @throws ContainerException + * @throws InvalidConfigException * @throws InvalidParamsException + * @throws ServiceNotFoundException + * @throws InvalidSignException */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[Epay][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Jsb][CallbackPlugin] 插件开始装载', ['rocket' => $rocket]); $this->formatRequestAndParams($rocket); $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $config = get_provider_config('jsb', $params); $payload = $rocket->getPayload(); $signature = $payload->get('sign'); @@ -43,51 +44,27 @@ public function assembly(Rocket $rocket, Closure $next): Rocket $payload->forget('sign'); $payload->forget('signType'); - $this->verifySign($config, $payload, $signature); + verify_jsb_sign($config, $payload->sortKeys()->toString(), $signature); $rocket->setDirection(NoHttpRequestDirection::class) ->setDestination($rocket->getPayload()); - Logger::info('[Epay][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][CallbackPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } - /** - * @throws InvalidConfigException - * @throws InvalidResponseException - */ - protected function verifySign(array $config, Collection $payload, ?string $signature = null): void - { - if (!$signature) { - throw new InvalidResponseException(Exception::SIGN_ERROR, 'Verify Epay payload Sign Failed: sign is empty', $payload); - } - - $publicCert = $config['epay_public_cert_path'] ?? null; - - if (empty($publicCert)) { - throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [epay_public_cert_path]'); - } - - $result = 1 === openssl_verify( - $payload->sortKeys()->toString(), - base64_decode($signature), - file_get_contents($publicCert) - ); - if (!$result) { - throw new InvalidConfigException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); - } - } - /** * @throws InvalidParamsException */ protected function formatRequestAndParams(Rocket $rocket): void { $request = $rocket->getParams()['request'] ?? null; + if (!$request instanceof Collection) { throw new InvalidParamsException(Exception::PARAMS_CALLBACK_REQUEST_INVALID); } + $rocket->setPayload($request)->setParams($rocket->getParams()['params'] ?? []); } } diff --git a/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php b/src/Plugin/Jsb/Pay/Scan/PayPlugin.php similarity index 54% rename from src/Plugin/Epay/Pay/Scan/PrepayPlugin.php rename to src/Plugin/Jsb/Pay/Scan/PayPlugin.php index d1c9fecee..5db29a3f8 100644 --- a/src/Plugin/Epay/Pay/Scan/PrepayPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/PayPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; +namespace Yansongda\Pay\Plugin\Jsb\Pay\Scan; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -15,7 +15,7 @@ use function Yansongda\Pay\get_provider_config; -class PrepayPlugin implements PluginInterface +class PayPlugin implements PluginInterface { /** * @throws InvalidConfigException @@ -24,25 +24,22 @@ class PrepayPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::debug('[Epay][Pay][Scan][PrepayPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::debug('[Jsb][Pay][Scan][PayPlugin] 插件开始装载', ['rocket' => $rocket]); - $backUrl = $rocket->getPayload()['notify_url'] ?? null; - if (empty($backUrl)) { - $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); - - $backUrl = $config['notify_url'] ?? null; - } + $params = $rocket->getParams(); + $config = get_provider_config('jsb', $params); + $backUrl = $rocket->getPayload()['notify_url'] ?? $config['notify_url'] ?? null; if (!$backUrl) { - throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [notify_url]'); + throw new InvalidConfigException(Exception::CONFIG_JSB_INVALID, '配置异常: 缺少配置参数 -- [notify_url]'); } + $rocket->mergePayload([ 'service' => 'atPay', 'backUrl' => $backUrl, ]); - Logger::info('[Epay][Pay][Scan][PrepayPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][Pay][Scan][PayPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php b/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php similarity index 64% rename from src/Plugin/Epay/Pay/Scan/QueryPlugin.php rename to src/Plugin/Jsb/Pay/Scan/QueryPlugin.php index 428619096..574096e61 100644 --- a/src/Plugin/Epay/Pay/Scan/QueryPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; +namespace Yansongda\Pay\Plugin\Jsb\Pay\Scan; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -13,14 +13,14 @@ class QueryPlugin implements PluginInterface { public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::debug('[Epay][Pay][Scan][QueryPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::debug('[Jsb][Pay][Scan][QueryPlugin] 插件开始装载', ['rocket' => $rocket]); $rocket->mergePayload([ 'deviceNo' => '1234567890', 'service' => 'payCheck', ]); - Logger::info('[Epay][Pay][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][Pay][Scan][QueryPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/Pay/Scan/RefundPlugin.php b/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php similarity index 64% rename from src/Plugin/Epay/Pay/Scan/RefundPlugin.php rename to src/Plugin/Jsb/Pay/Scan/RefundPlugin.php index d06a02f75..9a3fcb7b6 100644 --- a/src/Plugin/Epay/Pay/Scan/RefundPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay\Pay\Scan; +namespace Yansongda\Pay\Plugin\Jsb\Pay\Scan; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -13,14 +13,14 @@ class RefundPlugin implements PluginInterface { public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::debug('[Epay][Pay][Scan][RefundPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::debug('[Jsb][Pay][Scan][RefundPlugin] 插件开始装载', ['rocket' => $rocket]); $rocket->mergePayload([ 'deviceNo' => '1234567890', 'service' => 'payRefund', ]); - Logger::info('[Epay][Pay][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][Pay][Scan][RefundPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/ResponsePlugin.php b/src/Plugin/Jsb/ResponsePlugin.php similarity index 71% rename from src/Plugin/Epay/ResponsePlugin.php rename to src/Plugin/Jsb/ResponsePlugin.php index 03a9096f4..023ba9538 100644 --- a/src/Plugin/Epay/ResponsePlugin.php +++ b/src/Plugin/Jsb/ResponsePlugin.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use Psr\Http\Message\ResponseInterface; use Yansongda\Artful\Contract\PluginInterface; -use Yansongda\Artful\Exception\Exception; use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; +use Yansongda\Pay\Exception\Exception; use Yansongda\Supports\Collection; class ResponsePlugin implements PluginInterface @@ -23,11 +23,11 @@ public function assembly(Rocket $rocket, Closure $next): Rocket /* @var Rocket $rocket */ $rocket = $next($rocket); - Logger::debug('[Epay][ResponsePlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::debug('[Jsb][ResponsePlugin] 插件开始装载', ['rocket' => $rocket]); $this->validateResponse($rocket); - Logger::info('[Epay][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][ResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); return $rocket; } @@ -39,13 +39,14 @@ protected function validateResponse(Rocket $rocket): void { $destination = $rocket->getDestination(); $destinationOrigin = $rocket->getDestinationOrigin(); + if ($destinationOrigin instanceof ResponseInterface && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) { - throw new InvalidResponseException(Exception::RESPONSE_ERROR, 'epay返回状态码异常,请检查参数是否错误', $rocket->getDestination()); + throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '江苏银行返回状态码异常,请检查参数是否错误', $rocket->getDestination()); } if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) { - throw new InvalidResponseException(Exception::RESPONSE_ERROR, sprintf('epay返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); + throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, sprintf('江苏银行返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); } } } diff --git a/src/Plugin/Epay/StartPlugin.php b/src/Plugin/Jsb/StartPlugin.php similarity index 73% rename from src/Plugin/Epay/StartPlugin.php rename to src/Plugin/Jsb/StartPlugin.php index 902af5c48..22a1b1246 100644 --- a/src/Plugin/Epay/StartPlugin.php +++ b/src/Plugin/Jsb/StartPlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -23,14 +23,13 @@ class StartPlugin implements PluginInterface */ public function assembly(Rocket $rocket, Closure $next): Rocket { - Logger::info('[Epay][StartPlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Jsb][StartPlugin] 插件开始装载', ['rocket' => $rocket]); $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $config = get_provider_config('jsb', $params); - $rocket->setPacker(QueryPacker::class)->mergePayload(array_merge( - $params, - [ + $rocket->setPacker(QueryPacker::class) + ->mergePayload(array_merge($params, [ 'createData' => date('Ymd'), 'createTime' => date('His'), 'bizDate' => date('Ymd'), @@ -41,9 +40,9 @@ public function assembly(Rocket $rocket, Closure $next): Rocket 'publicKeyCode' => $config['public_key_code'] ?? '', 'version' => 'v1.0.0', 'charset' => 'utf-8', - ] - )); - Logger::info('[Epay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]); + ])); + + Logger::info('[Jsb][StartPlugin] 插件装载完毕', ['rocket' => $rocket]); return $next($rocket); } diff --git a/src/Plugin/Epay/VerifySignaturePlugin.php b/src/Plugin/Jsb/VerifySignaturePlugin.php similarity index 56% rename from src/Plugin/Epay/VerifySignaturePlugin.php rename to src/Plugin/Jsb/VerifySignaturePlugin.php index 36c6cf5fd..635cbf1dd 100644 --- a/src/Plugin/Epay/VerifySignaturePlugin.php +++ b/src/Plugin/Jsb/VerifySignaturePlugin.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yansongda\Pay\Plugin\Epay; +namespace Yansongda\Pay\Plugin\Jsb; use Closure; use Yansongda\Artful\Contract\PluginInterface; @@ -11,7 +11,6 @@ use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; -use Yansongda\Pay\Exception\Exception; use Yansongda\Pay\Exception\InvalidSignException; use Yansongda\Supports\Arr; use Yansongda\Supports\Collection; @@ -19,58 +18,38 @@ use function Yansongda\Artful\should_do_http_request; use function Yansongda\Pay\get_provider_config; +use function Yansongda\Pay\verify_jsb_sign; class VerifySignaturePlugin implements PluginInterface { /** + * @throws ContainerException + * @throws InvalidConfigException + * @throws InvalidSignException * @throws ServiceNotFoundException - * @throws ContainerException|InvalidConfigException|InvalidSignException */ public function assembly(Rocket $rocket, Closure $next): Rocket { /* @var Rocket $rocket */ $rocket = $next($rocket); - Logger::info('[Epay][VerifySignaturePlugin] 插件开始装载', ['rocket' => $rocket]); + Logger::info('[Jsb][VerifySignaturePlugin] 插件开始装载', ['rocket' => $rocket]); if (should_do_http_request($rocket->getDirection())) { $params = $rocket->getParams(); - $config = get_provider_config('epay', $params); + $config = get_provider_config('jsb', $params); + $body = (string) $rocket->getDestinationOrigin()->getBody(); - $this->verifySign($config, $body); + $signatureData = $this->getSignatureData($body); + + verify_jsb_sign($config, $signatureData['data'] ?? '', $signatureData['sign'] ?? ''); } - Logger::info('[Epay][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]); + Logger::info('[Jsb][VerifySignaturePlugin] 插件装载完毕', ['rocket' => $rocket]); return $rocket; } - /** - * @throws InvalidConfigException|InvalidSignException - */ - protected function verifySign(array $config, string $body): void - { - // 解析签名 - $signatureData = $this->getSignatureData($body); - - if (!$signatureData['sign']) { - throw new InvalidSignException(Exception::RESPONSE_MISSING_NECESSARY_PARAMS, 'Verify Epay Response Sign Failed: sign is empty', $body); - } - - $publicCert = $config['epay_public_cert_path'] ?? null; - if (empty($publicCert)) { - throw new InvalidConfigException(Exception::CONFIG_EPAY_INVALID, 'Missing Epay Config -- [epay_public_cert_path]'); - } - $result = 1 === openssl_verify( - $signatureData['data'], - base64_decode($signatureData['sign']), - file_get_contents($publicCert) - ); - if (!$result) { - throw new InvalidSignException(Exception::SIGN_ERROR, 'Verify Epay Response Sign Failed', func_get_args()); - } - } - private function getSignatureData(string $body): array { if (Str::contains($body, '&-&')) { diff --git a/src/Provider/Epay.php b/src/Provider/Jsb.php similarity index 71% rename from src/Provider/Epay.php rename to src/Provider/Jsb.php index 17b9d2912..11908465e 100644 --- a/src/Provider/Epay.php +++ b/src/Provider/Jsb.php @@ -13,6 +13,7 @@ use Yansongda\Artful\Event; use Yansongda\Artful\Exception\ContainerException; use Yansongda\Artful\Exception\InvalidParamsException; +use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Artful\Plugin\ParserPlugin; use Yansongda\Artful\Rocket; use Yansongda\Pay\Contract\ProviderInterface; @@ -20,28 +21,36 @@ use Yansongda\Pay\Event\MethodCalled; use Yansongda\Pay\Exception\Exception; use Yansongda\Pay\Pay; -use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; -use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; -use Yansongda\Pay\Plugin\Epay\CallbackPlugin; -use Yansongda\Pay\Plugin\Epay\ResponsePlugin; -use Yansongda\Pay\Plugin\Epay\StartPlugin; -use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; +use Yansongda\Pay\Plugin\Jsb\AddPayloadSignPlugin; +use Yansongda\Pay\Plugin\Jsb\AddRadarPlugin; +use Yansongda\Pay\Plugin\Jsb\CallbackPlugin; +use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; +use Yansongda\Pay\Plugin\Jsb\StartPlugin; +use Yansongda\Pay\Plugin\Jsb\VerifySignaturePlugin; use Yansongda\Supports\Collection; use Yansongda\Supports\Str; /** * @method Collection|Rocket scan(array $order) 扫码支付[微信支付宝都可扫描] */ -class Epay implements ProviderInterface +class Jsb implements ProviderInterface { public const URL = [ Pay::MODE_NORMAL => 'https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm', Pay::MODE_SANDBOX => 'https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', ]; - public function __call($name, $params) + /** + * @param mixed $name + * @param mixed $params + * + * @throws ContainerException + * @throws InvalidParamsException + * @throws ServiceNotFoundException + */ + public function __call($name, $params): null|Collection|MessageInterface|Rocket { - $plugin = '\Yansongda\Pay\Shortcut\Epay\\'.Str::studly($name).'Shortcut'; + $plugin = '\Yansongda\Pay\Shortcut\Jsb\\'.Str::studly($name).'Shortcut'; return Artful::shortcut($plugin, ...$params); } @@ -69,7 +78,7 @@ public function mergeCommonPlugins(array $plugins): array */ public function cancel(array $order): Collection|Rocket { - throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support cancel api'); + throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Jsb does not support cancel api'); } /** @@ -77,12 +86,17 @@ public function cancel(array $order): Collection|Rocket */ public function close(array $order): Collection|Rocket { - throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Epay does not support close api'); + throw new InvalidParamsException(Exception::PARAMS_METHOD_NOT_SUPPORTED, 'Jsb does not support close api'); } + /** + * @throws ContainerException + * @throws InvalidParamsException + * @throws ServiceNotFoundException + */ public function refund(array $order): Collection|Rocket { - Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); + Event::dispatch(new MethodCalled('jsb', __METHOD__, $order, null)); return $this->__call('refund', [$order]); } @@ -95,7 +109,7 @@ public function callback(null|array|ServerRequestInterface $contents = null, ?ar { $request = $this->getCallbackParams($contents); - Event::dispatch(new CallbackReceived('epay', $request->all(), $params, null)); + Event::dispatch(new CallbackReceived('jsb', $request->all(), $params, null)); return $this->pay( [CallbackPlugin::class], @@ -112,9 +126,14 @@ public function success(): ResponseInterface ); } + /** + * @throws ContainerException + * @throws InvalidParamsException + * @throws ServiceNotFoundException + */ public function query(array $order): Collection|Rocket { - Event::dispatch(new MethodCalled('epay', __METHOD__, $order, null)); + Event::dispatch(new MethodCalled('jsb', __METHOD__, $order, null)); return $this->__call('query', [$order]); } diff --git a/src/Service/EpayServiceProvider.php b/src/Service/JsbServiceProvider.php similarity index 61% rename from src/Service/EpayServiceProvider.php rename to src/Service/JsbServiceProvider.php index eecc09d2c..67997424d 100644 --- a/src/Service/EpayServiceProvider.php +++ b/src/Service/JsbServiceProvider.php @@ -7,18 +7,18 @@ use Yansongda\Artful\Contract\ServiceProviderInterface; use Yansongda\Artful\Exception\ContainerException; use Yansongda\Pay\Pay; -use Yansongda\Pay\Provider\Epay; +use Yansongda\Pay\Provider\Jsb; -class EpayServiceProvider implements ServiceProviderInterface +class JsbServiceProvider implements ServiceProviderInterface { /** * @throws ContainerException */ public function register(mixed $data = null): void { - $service = new Epay(); + $service = new Jsb(); - Pay::set(Epay::class, $service); - Pay::set('epay', $service); + Pay::set(Jsb::class, $service); + Pay::set('jsb', $service); } } diff --git a/src/Shortcut/Epay/QueryShortcut.php b/src/Shortcut/Jsb/QueryShortcut.php similarity index 61% rename from src/Shortcut/Epay/QueryShortcut.php rename to src/Shortcut/Jsb/QueryShortcut.php index 7e24ff5a1..83af51c83 100644 --- a/src/Shortcut/Epay/QueryShortcut.php +++ b/src/Shortcut/Jsb/QueryShortcut.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace Yansongda\Pay\Shortcut\Epay; +namespace Yansongda\Pay\Shortcut\Jsb; use Yansongda\Artful\Contract\ShortcutInterface; use Yansongda\Artful\Plugin\ParserPlugin; -use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; -use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; -use Yansongda\Pay\Plugin\Epay\Pay\Scan\QueryPlugin; -use Yansongda\Pay\Plugin\Epay\ResponsePlugin; -use Yansongda\Pay\Plugin\Epay\StartPlugin; -use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; +use Yansongda\Pay\Plugin\Jsb\AddPayloadSignPlugin; +use Yansongda\Pay\Plugin\Jsb\AddRadarPlugin; +use Yansongda\Pay\Plugin\Jsb\Pay\Scan\QueryPlugin; +use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; +use Yansongda\Pay\Plugin\Jsb\StartPlugin; +use Yansongda\Pay\Plugin\Jsb\VerifySignaturePlugin; class QueryShortcut implements ShortcutInterface { diff --git a/src/Shortcut/Epay/RefundShortcut.php b/src/Shortcut/Jsb/RefundShortcut.php similarity index 61% rename from src/Shortcut/Epay/RefundShortcut.php rename to src/Shortcut/Jsb/RefundShortcut.php index 56e0f5c1e..49ef59305 100644 --- a/src/Shortcut/Epay/RefundShortcut.php +++ b/src/Shortcut/Jsb/RefundShortcut.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace Yansongda\Pay\Shortcut\Epay; +namespace Yansongda\Pay\Shortcut\Jsb; use Yansongda\Artful\Contract\ShortcutInterface; use Yansongda\Artful\Plugin\ParserPlugin; -use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; -use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; -use Yansongda\Pay\Plugin\Epay\Pay\Scan\RefundPlugin; -use Yansongda\Pay\Plugin\Epay\ResponsePlugin; -use Yansongda\Pay\Plugin\Epay\StartPlugin; -use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; +use Yansongda\Pay\Plugin\Jsb\AddPayloadSignPlugin; +use Yansongda\Pay\Plugin\Jsb\AddRadarPlugin; +use Yansongda\Pay\Plugin\Jsb\Pay\Scan\RefundPlugin; +use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; +use Yansongda\Pay\Plugin\Jsb\StartPlugin; +use Yansongda\Pay\Plugin\Jsb\VerifySignaturePlugin; class RefundShortcut implements ShortcutInterface { diff --git a/src/Shortcut/Epay/ScanShortcut.php b/src/Shortcut/Jsb/ScanShortcut.php similarity index 57% rename from src/Shortcut/Epay/ScanShortcut.php rename to src/Shortcut/Jsb/ScanShortcut.php index 10aa52b1f..56ca79fa5 100644 --- a/src/Shortcut/Epay/ScanShortcut.php +++ b/src/Shortcut/Jsb/ScanShortcut.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace Yansongda\Pay\Shortcut\Epay; +namespace Yansongda\Pay\Shortcut\Jsb; use Yansongda\Artful\Contract\ShortcutInterface; use Yansongda\Artful\Plugin\ParserPlugin; -use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; -use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; -use Yansongda\Pay\Plugin\Epay\Pay\Scan\PrepayPlugin; -use Yansongda\Pay\Plugin\Epay\ResponsePlugin; -use Yansongda\Pay\Plugin\Epay\StartPlugin; -use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; +use Yansongda\Pay\Plugin\Jsb\AddPayloadSignPlugin; +use Yansongda\Pay\Plugin\Jsb\AddRadarPlugin; +use Yansongda\Pay\Plugin\Jsb\Pay\Scan\PayPlugin; +use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; +use Yansongda\Pay\Plugin\Jsb\StartPlugin; +use Yansongda\Pay\Plugin\Jsb\VerifySignaturePlugin; class ScanShortcut implements ShortcutInterface { @@ -19,7 +19,7 @@ public function getPlugins(array $params): array { return [ StartPlugin::class, - PrepayPlugin::class, + PayPlugin::class, AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php index 6a88ceec5..7229dfe24 100644 --- a/tests/FunctionTest.php +++ b/tests/FunctionTest.php @@ -46,7 +46,7 @@ use function Yansongda\Pay\verify_unipay_sign_qra; use function Yansongda\Pay\verify_wechat_sign; use function Yansongda\Pay\verify_wechat_sign_v2; -use function Yansongda\Pay\get_epay_url; +use function Yansongda\Pay\get_jsb_url; class FunctionTest extends TestCase { @@ -689,8 +689,8 @@ public function testVerifyUnipaySignQraEmpty() public function testGetEpayUrl() { - self::assertEquals('https://yansongda.cn', get_epay_url([], new Collection(['_url' => 'https://yansongda.cn']))); - self::assertEquals('https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm', get_epay_url(['mode' => Pay::MODE_NORMAL], new Collection())); - self::assertEquals('https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', get_epay_url(['mode' => Pay::MODE_SANDBOX], new Collection())); + self::assertEquals('https://yansongda.cn', get_jsb_url([], new Collection(['_url' => 'https://yansongda.cn']))); + self::assertEquals('https://mybank.jsbchina.cn:577/eis/merchant/merchantServices.htm', get_jsb_url(['mode' => Pay::MODE_NORMAL], new Collection())); + self::assertEquals('https://epaytest.jsbchina.cn:9999/eis/merchant/merchantServices.htm', get_jsb_url(['mode' => Pay::MODE_SANDBOX], new Collection())); } } diff --git a/tests/PayTest.php b/tests/PayTest.php index 0f3612d0e..b274e70de 100644 --- a/tests/PayTest.php +++ b/tests/PayTest.php @@ -8,7 +8,7 @@ use Yansongda\Artful\Exception\ServiceNotFoundException; use Yansongda\Pay\Pay; use Yansongda\Pay\Provider\Alipay; -use Yansongda\Pay\Provider\Epay; +use Yansongda\Pay\Provider\Jsb; use Yansongda\Pay\Provider\Unipay; use Yansongda\Pay\Provider\Wechat; @@ -35,8 +35,8 @@ public function testConfig() self::assertInstanceOf(Wechat::class, Pay::get(Wechat::class)); self::assertInstanceOf(Unipay::class, Pay::get('unipay')); self::assertInstanceOf(Unipay::class, Pay::get(Unipay::class)); - self::assertInstanceOf(Epay::class, Pay::get('epay')); - self::assertInstanceOf(Epay::class, Pay::get(Epay::class)); + self::assertInstanceOf(Jsb::class, Pay::get('jsb')); + self::assertInstanceOf(Jsb::class, Pay::get(Jsb::class)); // force $result1 = Pay::config(['name' => 'yansongda1', '_force' => true]); diff --git a/tests/Plugin/Epay/AddPayloadSignPluginTest.php b/tests/Plugin/Jsb/AddPayloadSignPluginTest.php similarity index 85% rename from tests/Plugin/Epay/AddPayloadSignPluginTest.php rename to tests/Plugin/Jsb/AddPayloadSignPluginTest.php index 020c81bbc..5101d0710 100644 --- a/tests/Plugin/Epay/AddPayloadSignPluginTest.php +++ b/tests/Plugin/Jsb/AddPayloadSignPluginTest.php @@ -1,6 +1,6 @@ setParams([])->setPayload(new Collection()); @@ -55,8 +55,8 @@ public function testMissMchSecretCertPath() Pay::set(ConfigInterface::class, new Config()); self::expectException(InvalidConfigException::class); - self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); - self::expectExceptionMessage('参数异常: epay支付配置文件中未找到 `mch_secret_cert_path` 配置项。可能插件用错顺序,应该先使用 `StartPlugin`'); + self::expectExceptionCode(Exception::CONFIG_JSB_INVALID); + self::expectExceptionMessage('配置异常: 缺少配置参数 -- [mch_secret_cert_path]'); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($sign, $result->getPayload()->get('sign')); } diff --git a/tests/Plugin/Epay/AddRadarPluginTest.php b/tests/Plugin/Jsb/AddRadarPluginTest.php similarity index 93% rename from tests/Plugin/Epay/AddRadarPluginTest.php rename to tests/Plugin/Jsb/AddRadarPluginTest.php index 58deb7255..76ca9cb89 100644 --- a/tests/Plugin/Epay/AddRadarPluginTest.php +++ b/tests/Plugin/Jsb/AddRadarPluginTest.php @@ -1,9 +1,9 @@ '6a13eab71c4f4b0aa4757eda6fc59710', @@ -80,8 +81,8 @@ public function testErrorSign() public function testEmptySign() { - self::expectException(InvalidResponseException::class); - self::expectExceptionCode(Exception::SIGN_ERROR); + self::expectException(InvalidSignException::class); + self::expectExceptionCode(Exception::SIGN_EMPTY); $payload = [ 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', 'orderStatus'=> '1', @@ -125,7 +126,7 @@ public function testErrorRequestType() public function testMissingEpayPublicCertPath() { self::expectException(InvalidConfigException::class); - self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); + self::expectExceptionCode(Exception::CONFIG_JSB_INVALID); Pay::set(ConfigInterface::class, new Config()); $payload = [ 'partnerId'=> '6a13eab71c4f4b0aa4757eda6fc59710', diff --git a/tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php b/tests/Plugin/Jsb/Pay/Scan/PayPluginTest.php similarity index 64% rename from tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php rename to tests/Plugin/Jsb/Pay/Scan/PayPluginTest.php index 6b5bfa0f5..f3b35b624 100644 --- a/tests/Plugin/Epay/Pay/Scan/PrepayPluginTest.php +++ b/tests/Plugin/Jsb/Pay/Scan/PayPluginTest.php @@ -1,21 +1,21 @@ plugin = new PrepayPlugin(); + $this->plugin = new PayPlugin(); } public function testNormal() diff --git a/tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php b/tests/Plugin/Jsb/Pay/Scan/QueryPluginTest.php similarity index 84% rename from tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php rename to tests/Plugin/Jsb/Pay/Scan/QueryPluginTest.php index 4c4676a70..226af8961 100644 --- a/tests/Plugin/Epay/Pay/Scan/QueryPluginTest.php +++ b/tests/Plugin/Jsb/Pay/Scan/QueryPluginTest.php @@ -1,9 +1,9 @@ setDestinationOrigin(new Response(500, [], $body)); + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); @@ -44,8 +44,8 @@ public function testCodeWrong() public function testHttpWrong() { self::expectException(InvalidResponseException::class); - self::expectExceptionCode(Exception::RESPONSE_ERROR); - self::expectExceptionMessage('epay返回状态码异常,请检查参数是否错误'); + self::expectExceptionCode(Exception::RESPONSE_CODE_WRONG); + self::expectExceptionMessage('江苏银行返回状态码异常,请检查参数是否错误'); $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)); diff --git a/tests/Plugin/Epay/StartPluginTest.php b/tests/Plugin/Jsb/StartPluginTest.php similarity index 91% rename from tests/Plugin/Epay/StartPluginTest.php rename to tests/Plugin/Jsb/StartPluginTest.php index 6398c893b..6edb69e32 100644 --- a/tests/Plugin/Epay/StartPluginTest.php +++ b/tests/Plugin/Jsb/StartPluginTest.php @@ -1,10 +1,10 @@ setDestinationOrigin(new Response(200, [], $body)); Pay::set(ConfigInterface::class, new Config()); self::expectException(InvalidConfigException::class); - self::expectExceptionCode(Exception::CONFIG_EPAY_INVALID); - self::expectExceptionMessage('Missing Epay Config -- [epay_public_cert_path]'); + self::expectExceptionCode(Exception::CONFIG_JSB_INVALID); + self::expectExceptionMessage('配置异常: 缺少配置参数 -- [jsb_public_cert_path]'); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); diff --git a/tests/Provider/EpayTest.php b/tests/Provider/JsbTest.php similarity index 91% rename from tests/Provider/EpayTest.php rename to tests/Provider/JsbTest.php index a4a4d7dc5..40d495828 100644 --- a/tests/Provider/EpayTest.php +++ b/tests/Provider/JsbTest.php @@ -15,22 +15,22 @@ use Yansongda\Artful\Exception\InvalidResponseException; use Yansongda\Artful\Plugin\ParserPlugin; use Yansongda\Pay\Pay; -use Yansongda\Pay\Plugin\Epay\AddPayloadSignPlugin; -use Yansongda\Pay\Plugin\Epay\AddRadarPlugin; -use Yansongda\Pay\Plugin\Epay\ResponsePlugin; -use Yansongda\Pay\Plugin\Epay\StartPlugin; -use Yansongda\Pay\Plugin\Epay\VerifySignaturePlugin; +use Yansongda\Pay\Plugin\Jsb\AddPayloadSignPlugin; +use Yansongda\Pay\Plugin\Jsb\AddRadarPlugin; +use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; +use Yansongda\Pay\Plugin\Jsb\StartPlugin; +use Yansongda\Pay\Plugin\Jsb\VerifySignaturePlugin; use Yansongda\Pay\Tests\Stubs\Plugin\FooPluginStub; use Yansongda\Pay\Tests\TestCase; -class EpayTest extends TestCase +class JsbTest extends TestCase { public function testShortcutNotFound() { self::expectException(InvalidParamsException::class); self::expectExceptionCode(Exception::PARAMS_SHORTCUT_INVALID); - Pay::epay()->foo(); + Pay::jsb()->foo(); } public function testMergeCommonPlugins() @@ -42,7 +42,7 @@ public function testMergeCommonPlugins() [StartPlugin::class], $plugins, [AddPayloadSignPlugin::class, AddRadarPlugin::class, VerifySignaturePlugin::class, ResponsePlugin::class, ParserPlugin::class], - ), Pay::epay()->mergeCommonPlugins($plugins)); + ), Pay::jsb()->mergeCommonPlugins($plugins)); } public function testScan() @@ -53,7 +53,7 @@ public function testScan() $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); Pay::set(HttpClientInterface::class, $http); - $result = Pay::epay()->scan([ + $result = Pay::jsb()->scan([ 'outTradeNo' => 'YC202406170003', 'totalFee' => 0.01, 'proInfo' => '充值', @@ -69,7 +69,7 @@ public function testQuery() Pay::set(HttpClientInterface::class, $http); $outTradeNo = 'YC202406170003'; - $result = Pay::epay()->query([ + $result = Pay::jsb()->query([ 'outTradeNo' => $outTradeNo, ]); @@ -87,7 +87,7 @@ public function testRefundErrorRefundAmt() $http->shouldReceive('sendRequest')->andReturn(new Response(200, [], $response)); Pay::set(HttpClientInterface::class, $http); $outTradeNo = 'YC202406170003'; - Pay::epay()->refund([ + Pay::jsb()->refund([ 'outTradeNo' => $outTradeNo, 'refundAmt' => 0.02, ]); @@ -102,7 +102,7 @@ public function testRefund() Pay::set(HttpClientInterface::class, $http); $outRefundNo = 'RK-YC202406170004'; - $result = Pay::epay()->refund([ + $result = Pay::jsb()->refund([ 'outTradeNo' => 'YC202406170004', 'refundAmt' => 0.01, 'outRefundNo' => $outRefundNo, @@ -117,7 +117,7 @@ public function testCancel() { $this->expectException(InvalidParamsException::class); - Pay::epay()->cancel([ + Pay::jsb()->cancel([ 'outTradeNo' => 'YC202406170003', ]); } @@ -126,7 +126,7 @@ public function testClose() { $this->expectException(InvalidParamsException::class); - Pay::epay()->close([ + Pay::jsb()->close([ 'outTradeNo' => 'YC202406170003', ]); } @@ -145,7 +145,7 @@ public function testCallback() 'signType'=> 'RSA', 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' ]; - $result = Pay::epay()->callback($payload); + $result = Pay::jsb()->callback($payload); self::assertNotEmpty($result->all()); self::assertArrayHasKey('outTradeNo', $result->all()); self::assertArrayHasKey('orderNo', $result->all()); @@ -169,7 +169,7 @@ public function testCallbackByFromGlobals() 'sign'=> 'DPKX4mZAVd/LwMDOt1OJgryBuPeH78y7B78smze+m+vvzae5MBf0O3BoTvVJQHD/RPVftHVvnYHeKvIjCC2bCrxoY9Sv2N8Hbr5HfjIikk0a2qaIQp6TTvecMP9JitzSuZP+sih+uxMkRM5Nrg8weGbePaQ6nODNWiSGDhV+Jq0=' ]; $_POST = array_merge($_POST, $payload); - $result = Pay::epay()->callback(); + $result = Pay::jsb()->callback(); self::assertNotEmpty($result->all()); self::assertArrayHasKey('outTradeNo', $result->all()); self::assertArrayHasKey('orderNo', $result->all()); @@ -194,7 +194,7 @@ public function testCallbackByServerRequest() ]; $serverRequest = new ServerRequest('POST', 'http://localhost', [], null); $serverRequest = $serverRequest->withParsedBody($payload); - $result = Pay::epay()->callback($serverRequest); + $result = Pay::jsb()->callback($serverRequest); self::assertNotEmpty($result->all()); self::assertArrayHasKey('outTradeNo', $result->all()); self::assertArrayHasKey('orderNo', $result->all()); @@ -205,7 +205,7 @@ public function testCallbackByServerRequest() public function testSuccess() { - $result = Pay::epay()->success(); + $result = Pay::jsb()->success(); self::assertInstanceOf(ResponseInterface::class, $result); self::assertStringContainsString('success', (string) $result->getBody()); diff --git a/tests/Shortcut/Epay/QueryShortcutTest.php b/tests/Shortcut/Jsb/QueryShortcutTest.php similarity index 58% rename from tests/Shortcut/Epay/QueryShortcutTest.php rename to tests/Shortcut/Jsb/QueryShortcutTest.php index e5c1540e8..7c00dd7cc 100644 --- a/tests/Shortcut/Epay/QueryShortcutTest.php +++ b/tests/Shortcut/Jsb/QueryShortcutTest.php @@ -1,15 +1,15 @@ Pay::MODE_SANDBOX, ], ], - 'epay' => [ + 'jsb' => [ 'default' => [ // 服务代码 'svr_code' => '', @@ -165,7 +165,7 @@ protected function setUp(): void // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) 'mch_public_cert_path' => __DIR__.'/Cert/EpayCert.cer', // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) - 'epay_public_cert_path' => __DIR__.'/Cert/jschina.cer', + 'jsb_public_cert_path' => __DIR__.'/Cert/jschina.cer', 'notify_url' => 'http://127.0.0.1:8000/epay/return', // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:test环境, 'mode' => Pay::MODE_SANDBOX, diff --git a/web/.vitepress/sidebar/v3.js b/web/.vitepress/sidebar/v3.js index 72f0dd991..faefb64fe 100644 --- a/web/.vitepress/sidebar/v3.js +++ b/web/.vitepress/sidebar/v3.js @@ -20,6 +20,7 @@ export default [ { text: '支付宝', link: '/docs/v3/quick-start/alipay' }, { text: '微信', link: '/docs/v3/quick-start/wechat' }, { text: '银联', link: '/docs/v3/quick-start/unipay' }, + { text: '江苏银行', link: '/docs/v3/quick-start/jsb' }, { text: '返回格式', link: '/docs/v3/quick-start/return-format' } ] }, @@ -65,6 +66,18 @@ export default [ { text: '所有内置插件', link: '/docs/v3/unipay/all' } ] }, + { + text: '江苏银行', + collapsed: true, + items: [ + { text: '支付', link: '/docs/v3/jsb/pay' }, + { text: '查询', link: '/docs/v3/jsb/query' }, + { text: '退款', link: '/docs/v3/jsb/refund' }, + { text: '接收回调', link: '/docs/v3/jsb/callback' }, + { text: '确认回调', link: '/docs/v3/jsb/response' }, + { text: '所有内置插件', link: '/docs/v3/jsb/all' } + ] + }, { text: '核心架构', collapsed: false, diff --git a/web/docs/v3/epay/all.md b/web/docs/v3/jsb/all.md similarity index 69% rename from web/docs/v3/epay/all.md rename to web/docs/v3/jsb/all.md index b7c8fa424..5e3481a7a 100644 --- a/web/docs/v3/epay/all.md +++ b/web/docs/v3/jsb/all.md @@ -1,10 +1,10 @@ -# e融支付更多方便的插件 +# 江苏银行e融支付更多方便的插件 得益于 yansongda/pay 的基础架构和良好的插件机制, 您可以自有的使用任何内置插件和自定义插件调用支付宝的任何 API。 诸如签名、API调用、解密、验签、解包等基础插件已经内置在 Pay 中, -您可以使用 `Pay::epay()->mergeCommonPlugins(array $plugins)` 来获取调用 API 所必须的常用插件 +您可以使用 `Pay::jsb()->mergeCommonPlugins(array $plugins)` 来获取调用 API 所必须的常用插件 首先,查找你想使用的插件,然后 @@ -24,16 +24,16 @@ $result = Pay::epay()->pay($allPlugins, $params); ## 支付 -### 扫码支付(聚合支付, 支持支付宝、微信、银联、e融支付) +### 扫码支付(聚合支付, 支持支付宝、微信、银联、江苏银行e融支付) - 交易预创建 - `\Yansongda\Pay\Plugin\Epay\Pay\Scan\PrepayPlugin` + `\Yansongda\Pay\Plugin\Jsb\Pay\Scan\PayPlugin` - 交易退款接口 - `\Yansongda\Pay\Plugin\Epay\Pay\Scan\RefundPlugin` + `\Yansongda\Pay\Plugin\Jsb\Pay\Scan\RefundPlugin` - 交易/退款结果查询 - `\Yansongda\Pay\Plugin\Epay\Pay\Scan\QueryPlugin` + `\Yansongda\Pay\Plugin\Jsb\Pay\Scan\QueryPlugin` diff --git a/web/docs/v3/epay/callback.md b/web/docs/v3/jsb/callback.md similarity index 85% rename from web/docs/v3/epay/callback.md rename to web/docs/v3/jsb/callback.md index d3e827c00..ca9092454 100644 --- a/web/docs/v3/epay/callback.md +++ b/web/docs/v3/jsb/callback.md @@ -1,18 +1,16 @@ -# 接收e融支付回调 +# 接收江苏银行e融支付回调 | 方法名 | 参数 | 返回值 | |:--------:|:------------------------------:|:----------:| | callback | 无/array/ServerRequestInterface | Collection | -使用的加密方式为支付宝官方推荐的 **RSA2**,目前只支持这一种加密方式,且没有支持其他加密方式的计划。 - ## 例子 ```php Pay::config($this->config); // 是的,你没有看错,就是这么简单! -$result = Pay::epay()->callback(); +$result = Pay::jsb()->callback(); ``` ## 参数 diff --git a/web/docs/v3/epay/pay.md b/web/docs/v3/jsb/pay.md similarity index 79% rename from web/docs/v3/epay/pay.md rename to web/docs/v3/jsb/pay.md index 333753163..a2df55b8b 100644 --- a/web/docs/v3/epay/pay.md +++ b/web/docs/v3/jsb/pay.md @@ -1,6 +1,6 @@ -# e融支付 +# 江苏银行e融支付 -e融支付目前直接内置支持以下快捷方式支付方法,对应的支付 method 如下: +江苏银行e融支付目前直接内置支持以下快捷方式支付方法,对应的支付 method 如下: | method | 说明 | 参数 | 返回值 | |:--------:|:------:|:------------:|:----------:| @@ -15,11 +15,11 @@ e融支付目前直接内置支持以下快捷方式支付方法,对应的支 ```php Pay::config($this->config); -$result = Pay::epay()->scan([ +$result = Pay::jsb()->scan([ 'outTradeNo' => 'YC202406170003', 'totalFee' => 0.01, 'proInfo' => '充值' - ]); +]); //可通过$result->payUrl参数生成二维码支付链接 ``` diff --git a/web/docs/v3/epay/query.md b/web/docs/v3/jsb/query.md similarity index 79% rename from web/docs/v3/epay/query.md rename to web/docs/v3/jsb/query.md index b9ac40ca4..06607ba70 100644 --- a/web/docs/v3/epay/query.md +++ b/web/docs/v3/jsb/query.md @@ -1,4 +1,4 @@ -# e融支付查询订单(退款订单 or 交易支付订单) +# 江苏银行e融支付查询订单(退款订单 or 交易支付订单) | 方法名 | 参数 | 返回值 | |:-----:|:------------:|:----------:| @@ -13,7 +13,7 @@ Pay::config($this->config); $order = [ 'outTradeNo' => '1514027114', ]; -$result = Pay::epay()->query($order); +$result = Pay::jsb()->query($order); ``` ## 查询退款订单 @@ -26,7 +26,7 @@ $order = [ 'outTradeNo' => 'RK-1514027114', ]; -$result = Pay::epay()->query($order); +$result = Pay::jsb()->query($order); ``` ### 配置参数 diff --git a/web/docs/v3/epay/refund.md b/web/docs/v3/jsb/refund.md similarity index 89% rename from web/docs/v3/epay/refund.md rename to web/docs/v3/jsb/refund.md index 53e854a92..3db2d17d9 100644 --- a/web/docs/v3/epay/refund.md +++ b/web/docs/v3/jsb/refund.md @@ -1,4 +1,4 @@ -# 支付宝退款 +# 江苏银行e融支付退款 | 方法名 | 参数 | 返回值 | |:------:|:------------:|:----------:| @@ -13,7 +13,7 @@ ```php Pay::config($this->config); -$result = Pay::epay()->refund([ +$result = Pay::jsb()->refund([ 'outTradeNo' => 'YC202406170004', 'refundAmt' => 0.01, 'outRefundNo' => 'TK-YC202406170004', diff --git a/web/docs/v3/epay/response.md b/web/docs/v3/jsb/response.md similarity index 72% rename from web/docs/v3/epay/response.md rename to web/docs/v3/jsb/response.md index 20c534193..82f17f649 100644 --- a/web/docs/v3/epay/response.md +++ b/web/docs/v3/jsb/response.md @@ -1,4 +1,4 @@ -# e融支付确认回调 +# 江苏银行e融支付确认回调 | 方法名 | 参数 | 返回值 | |:-------:|:---:|:--------:| @@ -9,7 +9,7 @@ ```php Pay::config($config); -return Pay::epay()->success(); +return Pay::jsb()->success(); ``` ## 配置参数 diff --git a/web/docs/v3/quick-start/init.md b/web/docs/v3/quick-start/init.md index 6381290a0..239035f8b 100644 --- a/web/docs/v3/quick-start/init.md +++ b/web/docs/v3/quick-start/init.md @@ -96,6 +96,26 @@ $config = [ 'mode' => Pay::MODE_NORMAL, ], ], + 'jsb' => [ + 'default' => [ + // 服务代码 + 'svr_code' => '', + // 必填-合作商ID + 'partner_id' => '', + // 必填-公私钥对编号 + 'public_key_code' => '00', + // 必填-商户私钥(加密签名) + 'mch_secret_cert_path' => '', + // 必填-商户公钥证书路径(提供江苏银行进行验证签名用) + 'mch_public_cert_path' => '', + // 必填-江苏银行的公钥(用于解密江苏银行返回的数据) + 'jsb_public_cert_path' => '', + //支付通知地址 + 'notify_url' => '', + // 选填-默认为正常模式。可选为: MODE_NORMAL:正式环境, MODE_SANDBOX:测试环境 + 'mode' => Pay::MODE_NORMAL, + ] + ], 'logger' => [ 'enable' => false, 'file' => './logs/pay.log', diff --git a/web/docs/v3/quick-start/jsb.md b/web/docs/v3/quick-start/jsb.md new file mode 100644 index 000000000..661e8af3b --- /dev/null +++ b/web/docs/v3/quick-start/jsb.md @@ -0,0 +1,58 @@ +# 江苏银行e融支付快速入门 + +在初始化完毕后,就可以直接方便的享受 `yansongda/pay` 带来的便利了。 + +## 扫码支付 + +```php +Pay::config($this->config); + +$result = Pay::jsb()->scan([ + 'outTradeNo' => 'YC202406170003', + 'totalFee' => 0.01, + 'proInfo' => '充值' +]); + +return $result->payUrl; // 二维码 url +``` + +## 退款 + +```php +Pay::config($this->config); + +$result = Pay::jsb()->refund([ + 'outTradeNo' => 'YC202406170004', + 'refundAmt' => 0.01, + 'outRefundNo' => 'TK-YC202406170004', +]); +``` + +## 查询订单 + +```php +Pay::config($this->config); + +//查询交易支付订单 +$order = [ + 'outTradeNo' => '1514027114', +]; + +$result = Pay::jsb()->query($order); +``` + +## 回调处理 + +```php +Pay::config($this->config); + +$result = Pay::jsb()->callback(); +``` + +## 响应回调 + +```php +Pay::config($this->config); + +return Pay::jsb()->success(); +``` From 100f8dd53357ae87b26dde457f04b6ea96d4c3ef Mon Sep 17 00:00:00 2001 From: wanghaojie <814425737@qq.com> Date: Sat, 22 Jun 2024 09:34:36 +0800 Subject: [PATCH 15/17] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Plugin/Jsb/ResponsePlugin.php | 1 - tests/Plugin/Jsb/ResponsePluginTest.php | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Plugin/Jsb/ResponsePlugin.php b/src/Plugin/Jsb/ResponsePlugin.php index 023ba9538..23f982e8a 100644 --- a/src/Plugin/Jsb/ResponsePlugin.php +++ b/src/Plugin/Jsb/ResponsePlugin.php @@ -44,7 +44,6 @@ protected function validateResponse(Rocket $rocket): void && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) { throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '江苏银行返回状态码异常,请检查参数是否错误', $rocket->getDestination()); } - if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) { throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, sprintf('江苏银行返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); } diff --git a/tests/Plugin/Jsb/ResponsePluginTest.php b/tests/Plugin/Jsb/ResponsePluginTest.php index 29676efa1..a2bf2f16c 100644 --- a/tests/Plugin/Jsb/ResponsePluginTest.php +++ b/tests/Plugin/Jsb/ResponsePluginTest.php @@ -4,10 +4,13 @@ use GuzzleHttp\Psr7\Response; use Yansongda\Artful\Exception\InvalidResponseException; +use Yansongda\Artful\Plugin\ParserPlugin; use Yansongda\Artful\Rocket; use Yansongda\Pay\Exception\Exception; use Yansongda\Pay\Plugin\Jsb\ResponsePlugin; use Yansongda\Pay\Tests\TestCase; +use Yansongda\Supports\Arr; +use Yansongda\Supports\Collection; class ResponsePluginTest extends TestCase { @@ -24,7 +27,8 @@ public function testNormal() { $body = 'errCode=&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; - $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)) + ->setDestination(new Collection(Arr::wrapQuery($body))); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); } @@ -35,7 +39,8 @@ public function testCodeWrong() self::expectExceptionCode(Exception::RESPONSE_BUSINESS_CODE_WRONG); $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000001&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; - $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)); + $rocket = (new Rocket())->setDestinationOrigin(new Response(200, [], $body)) + ->setDestination(new Collection(Arr::wrapQuery($body))); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); @@ -48,7 +53,8 @@ public function testHttpWrong() self::expectExceptionMessage('江苏银行返回状态码异常,请检查参数是否错误'); $body = 'errCode=1&field1=&field2=&field3=&orderNo=20240617144526400259379&orderStatus=1&outTradeNo=YC202406170003&partnerId=6a13eab71c4f4b0aa4757eda6fc59710&payUrl=http://weixintest.jsbchina.cn/epcs/qr/login.htm?qrCode=2018060611052793473720240617144526688568&respBizDate=20240617&respCode=000000&respMsg=交易成功&totalFee=0.01&validTime=2&signType=RSA&sign=jN3Ha6J9UUIe9M0L/XeexEdaRL9GB6nMV12wNC7LQvTS6V4nKHj4Qzw6M8cNsA9L0Tb3QFT83B0qO3FJnruDrcHKqBLZb4FkoKKN/WiDBuA2UZQjG4+CBejoGJWfpkWSsei9tXUk36TB27lc2ZlYXSEwuuDwM7M9yvlYysc3fjg='; - $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)); + $rocket = (new Rocket())->setDestinationOrigin(new Response(500, [], $body)) + ->setDestination(new Collection(Arr::wrapQuery($body))); $result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; }); self::assertSame($rocket, $result); From 052092b37ed0874dd1c1a054b2e3f3fa07a8edd7 Mon Sep 17 00:00:00 2001 From: yansongda Date: Sat, 22 Jun 2024 10:17:18 +0800 Subject: [PATCH 16/17] update --- src/Plugin/Jsb/Pay/Scan/PayPlugin.php | 3 +++ src/Plugin/Jsb/Pay/Scan/QueryPlugin.php | 3 +++ src/Plugin/Jsb/Pay/Scan/RefundPlugin.php | 3 +++ src/Plugin/Jsb/ResponsePlugin.php | 1 + 4 files changed, 10 insertions(+) diff --git a/src/Plugin/Jsb/Pay/Scan/PayPlugin.php b/src/Plugin/Jsb/Pay/Scan/PayPlugin.php index 5db29a3f8..dc9392d11 100644 --- a/src/Plugin/Jsb/Pay/Scan/PayPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/PayPlugin.php @@ -15,6 +15,9 @@ use function Yansongda\Pay\get_provider_config; +/** + * @see https://github.com/yansongda/pay/pull/1002 + */ class PayPlugin implements PluginInterface { /** diff --git a/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php b/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php index 574096e61..47b4e8968 100644 --- a/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/QueryPlugin.php @@ -9,6 +9,9 @@ use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; +/** + * @see https://github.com/yansongda/pay/pull/1002 + */ class QueryPlugin implements PluginInterface { public function assembly(Rocket $rocket, Closure $next): Rocket diff --git a/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php b/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php index 9a3fcb7b6..e77081036 100644 --- a/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php +++ b/src/Plugin/Jsb/Pay/Scan/RefundPlugin.php @@ -9,6 +9,9 @@ use Yansongda\Artful\Logger; use Yansongda\Artful\Rocket; +/** + * @see https://github.com/yansongda/pay/pull/1002 + */ class RefundPlugin implements PluginInterface { public function assembly(Rocket $rocket, Closure $next): Rocket diff --git a/src/Plugin/Jsb/ResponsePlugin.php b/src/Plugin/Jsb/ResponsePlugin.php index 23f982e8a..023ba9538 100644 --- a/src/Plugin/Jsb/ResponsePlugin.php +++ b/src/Plugin/Jsb/ResponsePlugin.php @@ -44,6 +44,7 @@ protected function validateResponse(Rocket $rocket): void && ($destinationOrigin->getStatusCode() < 200 || $destinationOrigin->getStatusCode() >= 300)) { throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, '江苏银行返回状态码异常,请检查参数是否错误', $rocket->getDestination()); } + if ($destination instanceof Collection && '000000' !== $destination->get('respCode')) { throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, sprintf('江苏银行返回错误: respCode:%s respMsg:%s', $destination->get('respCode'), $destination->get('respMsg')), $rocket->getDestination()); } From 2e3cf421b0d19686049b83191e2b2a28826702ba Mon Sep 17 00:00:00 2001 From: yansongda Date: Sat, 22 Jun 2024 10:20:40 +0800 Subject: [PATCH 17/17] update --- web/docs/v3/overview/contribute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/docs/v3/overview/contribute.md b/web/docs/v3/overview/contribute.md index 042742c86..adbb1ea29 100644 --- a/web/docs/v3/overview/contribute.md +++ b/web/docs/v3/overview/contribute.md @@ -1,6 +1,6 @@ # 参与开发 -由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 +由于测试及使用环境的限制,本项目中只开发了「支付宝」、「微信支付」、「银联」、「江苏银行」的相关支付网关。 如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_**