Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增 v3 付款码服务商模式 #1010

Merged
merged 2 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## 3.7.7
## v3.7.8

### added

- feat: 新增 v3 付款码服务商模式(#1010)

## v3.7.7

### added

Expand Down
21 changes: 20 additions & 1 deletion src/Plugin/Wechat/V3/Pay/Pos/CancelPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
use Yansongda\Artful\Logger;
use Yansongda\Artful\Rocket;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Pay;
use Yansongda\Supports\Collection;

use function Yansongda\Pay\get_provider_config;
use function Yansongda\Pay\get_wechat_type_key;

/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
* @see https://pay.weixin.qq.com/docs/partner/apis/partner-code-payment-v3/partner/partner-reverse.html
*/
class CancelPlugin implements PluginInterface
{
Expand All @@ -41,12 +44,17 @@ public function assembly(Rocket $rocket, Closure $next): Rocket
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 付款码支付撤销订单,参数缺少 `out_trade_no`');
}

if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
$data = $this->service($payload, $params, $config);
}

$rocket->setPayload(array_merge(
[
'_method' => 'POST',
'_url' => 'v3/pay/transactions/out-trade-no/'.$outTradeNo.'/reverse',
'_service_url' => 'v3/pay/partner/transactions/out-trade-no/'.$outTradeNo.'/reverse',
],
$this->normal($params, $config)
$data ?? $this->normal($params, $config)
));

Logger::info('[Wechat][V3][Pay][Pos][CancelPlugin] 插件装载完毕', ['rocket' => $rocket]);
Expand All @@ -61,4 +69,15 @@ protected function normal(array $params, array $config): array
'mchid' => $config['mch_id'] ?? '',
];
}

protected function service(Collection $payload, array $params, array $config): array
{
$configKey = get_wechat_type_key($params);

return [
'sp_appid' => $config[$configKey] ?? '',
'sp_mchid' => $config['mch_id'] ?? '',
'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
];
}
}
28 changes: 27 additions & 1 deletion src/Plugin/Wechat/V3/Pay/Pos/PayPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
use Throwable;
use Yansongda\Artful\Contract\PluginInterface;
use Yansongda\Artful\Exception\ContainerException;
use Yansongda\Artful\Exception\InvalidParamsException;
use Yansongda\Artful\Exception\ServiceNotFoundException;
use Yansongda\Artful\Logger;
use Yansongda\Artful\Rocket;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Pay;
use Yansongda\Supports\Collection;

use function Yansongda\Pay\get_provider_config;
use function Yansongda\Pay\get_wechat_type_key;

/**
* @see https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/code-pay.html
* @see https://pay.weixin.qq.com/docs/partner/apis/partner-code-payment-v3/partner/partner-code-pay.html
*/
class PayPlugin implements PluginInterface
{
Expand All @@ -29,15 +34,25 @@ public function assembly(Rocket $rocket, Closure $next): Rocket
{
Logger::debug('[Wechat][V3][Pay][Pos][PayPlugin] 插件开始装载', ['rocket' => $rocket]);

$payload = $rocket->getPayload();
$params = $rocket->getParams();
$config = get_provider_config('wechat', $params);

if (is_null($payload)) {
throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, '参数异常: 付款码支付,参数为空');
}

if (Pay::MODE_SERVICE === ($config['mode'] ?? Pay::MODE_NORMAL)) {
$data = $this->service($payload, $params, $config);
}

$rocket->mergePayload(array_merge(
[
'_method' => 'POST',
'_url' => 'v3/pay/transactions/codepay',
'_service_url' => 'v3/pay/partner/transactions/codepay',
],
$this->normal($params, $config)
$data ?? $this->normal($params, $config)
));

Logger::info('[Wechat][V3][Pay][Pos][PayPlugin] 插件装载完毕', ['rocket' => $rocket]);
Expand All @@ -52,4 +67,15 @@ protected function normal(array $params, array $config): array
'mchid' => $config['mch_id'] ?? '',
];
}

protected function service(Collection $payload, array $params, array $config): array
{
$configKey = get_wechat_type_key($params);

return [
'sp_appid' => $config[$configKey] ?? '',
'sp_mchid' => $config['mch_id'] ?? '',
'sub_mchid' => $payload->get('sub_mchid', $config['sub_mch_id'] ?? ''),
];
}
}
59 changes: 47 additions & 12 deletions tests/Plugin/Wechat/V3/Pay/Pos/CancelPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ protected function setUp(): void
$this->plugin = new CancelPlugin();
}

public function testMissingOut()
{
$rocket = new Rocket();
$rocket->setPayload(new Collection([
'aaa' => 'aaa'
]));

self::expectException(InvalidParamsException::class);
self::expectExceptionCode(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
self::expectExceptionMessage('参数异常: 付款码支付撤销订单,参数缺少 `out_trade_no`');

$this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
}

public function testNormal()
{
$rocket = new Rocket();
Expand All @@ -31,25 +45,46 @@ public function testNormal()
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
$payload = $result->getPayload();

self::assertEqualsCanonicalizing([
'_method' => 'POST',
'_url' => 'v3/pay/transactions/out-trade-no/111/reverse',
'appid' => 'wx55955316af4ef13',
'mchid' => '1600314069'
], $payload->all());
self::assertEquals('POST', $payload->get('_method'));
self::assertEquals('v3/pay/transactions/out-trade-no/111/reverse', $payload->get('_url'));
self::assertEquals('wx55955316af4ef13', $payload->get('appid'));
self::assertEquals('1600314069', $payload->get('mchid'));
}

public function testMissingOut()
public function testService()
{
$rocket = new Rocket();
$rocket->setPayload(new Collection([
$rocket->setParams(['_config' => 'service_provider'])->setPayload(new Collection([
"out_trade_no" => "111",
'aaa' => 'aaa'
]));

self::expectException(InvalidParamsException::class);
self::expectExceptionCode(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
self::expectExceptionMessage('参数异常: 付款码支付撤销订单,参数缺少 `out_trade_no`');
$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
$payload = $result->getPayload();

$this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
self::assertEquals('POST', $payload->get('_method'));
self::assertEquals('v3/pay/partner/transactions/out-trade-no/111/reverse', $payload->get('_service_url'));
self::assertEquals('wx55955316af4ef13', $payload->get('sp_appid'));
self::assertEquals('1600314069', $payload->get('sp_mchid'));
self::assertEquals('1600314070', $payload->get('sub_mchid'));
}

public function testServiceParams()
{
$rocket = new Rocket();
$rocket->setParams(['_config' => 'service_provider'])->setPayload(new Collection([
'sub_mchid' => '1222',
"out_trade_no" => "111",
'aaa' => 'aaa'
]));

$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
$payload = $result->getPayload();

self::assertEquals('POST', $payload->get('_method'));
self::assertEquals('v3/pay/partner/transactions/out-trade-no/111/reverse', $payload->get('_service_url'));
self::assertEquals('wx55955316af4ef13', $payload->get('sp_appid'));
self::assertEquals('1600314069', $payload->get('sp_mchid'));
self::assertEquals('1222', $payload->get('sub_mchid'));
}
}
80 changes: 80 additions & 0 deletions tests/Plugin/Wechat/V3/Pay/Pos/PayPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Yansongda\Pay\Tests\Plugin\Wechat\V3\Pay\Pos;

use Yansongda\Artful\Contract\PackerInterface;
use Yansongda\Artful\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\Exception;
use Yansongda\Pay\Plugin\Wechat\V3\Pay\Pos\PayPlugin;
use Yansongda\Artful\Rocket;
use Yansongda\Pay\Tests\TestCase;
Expand All @@ -19,6 +21,17 @@ protected function setUp(): void
$this->plugin = new PayPlugin();
}

public function testEmptyPayload()
{
$rocket = new Rocket();

self::expectException(InvalidParamsException::class);
self::expectExceptionCode(Exception::PARAMS_NECESSARY_PARAMS_MISSING);
self::expectExceptionMessage('参数异常: 付款码支付,参数为空');

$this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
}

public function testNormal()
{
$rocket = new Rocket();
Expand Down Expand Up @@ -50,4 +63,71 @@ public function testNormal()
self::assertEquals(1, $payload->get('amount')['total']);
self::assertEquals('5678', $payload->get('scene_info')['id']);
}

public function testService()
{
$rocket = new Rocket();
$rocket->setParams(['_config' => 'service_provider'])->setPayload(new Collection([
'description' => 'test',
"out_trade_no" => "111",
'payer' => [
'auth_code' => '1234'
],
'amount' => [
'total' => 1,
],
'scene_info' => [
'id' => '5678'
],
]));

$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
$payload = $result->getPayload();

self::assertEquals(PackerInterface::class, $result->getPacker());
self::assertEquals('v3/pay/partner/transactions/codepay', $payload->get('_service_url'));
self::assertEquals('POST', $payload->get('_method'));
self::assertEquals('wx55955316af4ef13', $payload->get('sp_appid'));
self::assertEquals('1600314069', $payload->get('sp_mchid'));
self::assertEquals('1600314070', $payload->get('sub_mchid'));
self::assertEquals('111', $payload->get('out_trade_no'));
self::assertEquals('test', $payload->get('description'));
self::assertEquals('1234', $payload->get('payer')['auth_code']);
self::assertEquals(1, $payload->get('amount')['total']);
self::assertEquals('5678', $payload->get('scene_info')['id']);
}

public function testServiceParams()
{
$rocket = new Rocket();
$rocket->setParams(['_config' => 'service_provider'])->setPayload(new Collection([
'sub_mchid' => 'aaaa',
'description' => 'test',
"out_trade_no" => "111",
'payer' => [
'auth_code' => '1234'
],
'amount' => [
'total' => 1,
],
'scene_info' => [
'id' => '5678'
],
]));

$result = $this->plugin->assembly($rocket, function ($rocket) { return $rocket; });
$payload = $result->getPayload();

self::assertEquals(PackerInterface::class, $result->getPacker());
self::assertEquals('v3/pay/partner/transactions/codepay', $payload->get('_service_url'));
self::assertEquals('POST', $payload->get('_method'));
self::assertEquals('wx55955316af4ef13', $payload->get('sp_appid'));
self::assertEquals('1600314069', $payload->get('sp_mchid'));
self::assertEquals('aaaa', $payload->get('sub_mchid'));
self::assertEquals('111', $payload->get('out_trade_no'));
self::assertEquals('test', $payload->get('description'));
self::assertEquals('1234', $payload->get('payer')['auth_code']);
self::assertEquals(1, $payload->get('amount')['total']);
self::assertEquals('5678', $payload->get('scene_info')['id']);
}
}