| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- <?php
- namespace Tests\Integration;
- use App\Console\Commands\ImportPaymentProviderConfig;
- use App\Services\Payment\JdPayService;
- use App\Services\Payment\PaymentProviderConfig;
- use Illuminate\Encryption\Encrypter;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Facade;
- use Symfony\Component\Console\Tester\CommandTester;
- use Tests\Support\PaymentProviderTestCase;
- class PaymentProviderConfigImportTest extends PaymentProviderTestCase
- {
- private function command(array $options = []): CommandTester
- {
- $command = new ImportPaymentProviderConfig();
- $command->setLaravel($this->app);
- $tester = new CommandTester($command);
- $tester->execute($options);
- return $tester;
- }
- private function configs(): array
- {
- return DB::table('payment_providers')->orderBy('code')->pluck('config', 'code')->all();
- }
- public function test_default_preview_lists_only_field_names_and_never_writes_or_discloses_values(): void
- {
- $before = $this->configs();
- $result = $this->command();
- $this->assertSame(0, $result->getStatusCode());
- $output = $result->getDisplay();
- $this->assertStringContainsString('未写入数据库', $output);
- $this->assertStringContainsString('mch_id', $output);
- $this->assertStringContainsString('config:cache', $output);
- foreach (['sanjin', 'qianbao', 'jd', 'no', 'zimu'] as $code) $this->assertStringContainsString($code, $output);
- foreach (['env-jd-mch', 'env-jd-secret-1234', 'https://env.example.com/pay'] as $value) $this->assertStringNotContainsString($value, $output);
- $this->assertSame($before, $this->configs());
- $this->assertSame(0, DB::table('operation_audits')->count());
- }
- public function test_selected_company_import_preserves_exact_active_credentials_and_pending_callbacks(): void
- {
- config(['app.jd_pay_key' => ' fixture-key-with-spaces ', 'app.jd_pay_recharge_user_ids' => ' 7, 8 ']);
- $before = PaymentProviderConfig::snapshot('jd');
- DB::table('payment_orders')->insert(['type' => 1, 'channel' => 'JDpay', 'status' => 1]);
- $callback = ['userCode' => JdPayService::getMerchantId(), 'orderCode' => 'pending-order', 'amount' => '100.00', 'status' => '3'];
- $callback['sign'] = JdPayService::signature([$callback['orderCode'], $callback['amount'], $callback['userCode'], $callback['status']]);
- $result = $this->command(['--company' => ['jd'], '--write' => true]);
- $this->assertSame(0, $result->getStatusCode());
- $this->assertSame($before, PaymentProviderConfig::snapshot('jd'));
- $this->assertTrue(JdPayService::verifyPayNotify($callback));
- $this->assertSame(' fixture-key-with-spaces ', JdPayService::getSecret());
- $raw = $this->configs();
- $this->assertNotNull($raw['jd']);
- foreach (['no', 'sanjin', 'qianbao', 'zimu'] as $code) $this->assertNull($raw[$code]);
- $this->assertStringNotContainsString('fixture-key-with-spaces', $raw['jd']);
- $this->assertStringNotContainsString('fixture-key-with-spaces', $result->getDisplay());
- $audit = DB::table('operation_audits')->first();
- $this->assertSame('import_env', $audit->action);
- $this->assertSame('console:payment:config-import-env', $audit->operator_name);
- $changes = json_decode($audit->changes, true, 512, JSON_THROW_ON_ERROR);
- $this->assertSame('******', $changes['after']['key']);
- }
- public function test_existing_admin_values_including_empty_allowlist_win_and_repeated_import_is_idempotent(): void
- {
- (new PaymentProviderConfig())->save('jd', ['mch_id' => 'admin-merchant', 'key' => 'admin-secret', 'recharge_user_ids' => '']);
- config(['app.jd_pay_recharge_user_ids' => '99']);
- $before = PaymentProviderConfig::snapshot('jd');
- $result = $this->command(['--company' => ['jd'], '--write' => true]);
- $this->assertSame(0, $result->getStatusCode());
- $this->assertSame($before, PaymentProviderConfig::snapshot('jd'));
- $this->assertSame('admin-merchant', JdPayService::getMerchantId());
- $this->assertSame('admin-secret', JdPayService::getSecret());
- $this->assertSame('', PaymentProviderConfig::get('jd', 'recharge_user_ids'));
- $raw = $this->configs();
- $audits = DB::table('operation_audits')->count();
- $this->assertSame(0, $this->command(['--company' => ['jd', 'jd'], '--write' => true])->getStatusCode());
- $this->assertSame($raw, $this->configs());
- $this->assertSame($audits, DB::table('operation_audits')->count());
- }
- public function test_bulk_import_copies_only_nonempty_defaults_and_changes_no_effective_values(): void
- {
- $before = [];
- foreach (array_keys($this->configs()) as $code) $before[$code] = PaymentProviderConfig::snapshot($code);
- $this->assertSame(0, $this->command(['--write' => true])->getStatusCode());
- foreach ($before as $code => $snapshot) $this->assertSame($snapshot, PaymentProviderConfig::snapshot($code));
- $fields = array_column((new PaymentProviderConfig())->show('jd')['fields'], null, 'field');
- $this->assertSame('admin', $fields['mch_id']['source']);
- $this->assertSame('admin', $fields['key']['source']);
- $this->assertSame('empty', $fields['pay_query_gateway']['source']);
- $this->assertNull($this->configs()['zimu']);
- $this->assertSame('7,8', PaymentProviderConfig::get('no', 'recharge_user_ids'));
- }
- public function test_invalid_company_is_rejected_without_writing(): void
- {
- $before = $this->configs();
- $result = $this->command(['--company' => ['unknown'], '--write' => true]);
- $this->assertSame(1, $result->getStatusCode());
- $this->assertStringContainsString('代码无效', $result->getDisplay());
- $this->assertSame($before, $this->configs());
- $this->assertSame(0, DB::table('operation_audits')->count());
- }
- public function test_unreadable_later_company_rolls_back_the_whole_batch(): void
- {
- (new PaymentProviderConfig())->save('jd', ['mch_id' => 'admin-merchant', 'key' => 'admin-secret']);
- $before = $this->configs();
- $audits = DB::table('operation_audits')->count();
- $this->app->instance('encrypter', new Encrypter(str_repeat('x', 32), 'AES-256-CBC'));
- Facade::clearResolvedInstance('encrypter');
- $result = $this->command(['--write' => true]);
- $this->assertSame(1, $result->getStatusCode());
- $this->assertSame($before, $this->configs());
- $this->assertSame($audits, DB::table('operation_audits')->count());
- $this->assertStringNotContainsString('MAC', $result->getDisplay());
- $this->assertStringNotContainsString('admin-secret', $result->getDisplay());
- }
- public function test_missing_schema_returns_a_controlled_failure(): void
- {
- DB::statement('DROP TABLE bot_payment_providers');
- $result = $this->command(['--write' => true]);
- $this->assertSame(1, $result->getStatusCode());
- $this->assertStringContainsString('导入失败', $result->getDisplay());
- $this->assertStringNotContainsString('SQLSTATE', $result->getDisplay());
- }
- public function test_audit_failure_rolls_back_imported_secrets(): void
- {
- $before = $this->configs();
- DB::statement('DROP TABLE bot_operation_audits');
- $result = $this->command(['--company' => ['jd'], '--write' => true]);
- $this->assertSame(1, $result->getStatusCode());
- $this->assertSame($before, $this->configs());
- $this->assertStringNotContainsString('env-jd-secret-1234', $result->getDisplay());
- }
- }
|