setName('shopro_huifu:test-pay') ->setDescription('Test Huifu gateway connectivity and SDK init'); } protected function execute(Input $input, Output $output) { $output->writeln('=== 汇付连通性测试 ==='); if (!Bridge::isEnabled()) { $output->writeln('[FAIL] Bridge::isEnabled() = false'); return; } if (!Bridge::isHuifuMiniProgramChannel('wxMiniProgram')) { $output->writeln('[FAIL] miniprogram_channel 不是 huifu'); return; } $config = HuifuConfig::getInstance(); $gateway = $config->getGatewayUrl(); $output->writeln('网关: ' . $gateway); $start = microtime(true); $ch = curl_init($gateway); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_exec($ch); $errno = curl_errno($ch); $err = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $elapsed = round((microtime(true) - $start) * 1000); if ($errno) { $output->writeln("[FAIL] 无法连接汇付网关 ({$elapsed}ms): [{$errno}] {$err}"); $output->writeln('请检查服务器出网防火墙是否放行 api.huifu.com'); return; } $output->writeln("[OK] 汇付网关可达 HTTP {$httpCode} ({$elapsed}ms)"); try { HuifuConfig::getInstance()->validateKeys(); $output->writeln('[OK] RSA 密钥格式校验通过'); } catch (\Exception $e) { $output->writeln('[FAIL] RSA 密钥: ' . $e->getMessage()); $output->writeln('请重新粘贴汇付控台的商户私钥、汇付公钥(支持带或不带 PEM 头)'); return; } try { SdkLoader::boot(); $output->writeln('[OK] SDK 初始化成功'); } catch (\Exception $e) { $output->writeln('[FAIL] SDK 初始化: ' . $e->getMessage()); return; } $output->writeln(''); $output->writeln('=== 汇付签名 API 探针(查单) ==='); try { $probe = (new \addons\shopro_huifu\library\HuifuClient())->probeSignedApi(); if ($probe['ok']) { $output->writeln('[OK] ' . $probe['message']); } else { $output->writeln('[FAIL] ' . $probe['message']); } } catch (\Exception $e) { $output->writeln('[FAIL] ' . $e->getMessage()); } $appId = Bridge::getMiniProgramAppId(); $env = $config->get('env', 'prod'); $prodMode = defined('PROD_MODE') ? (PROD_MODE ? 'true' : 'false') : 'undefined'; $gateway = $prodMode === 'true' ? 'https://api.huifu.com (生产)' : 'https://spin-test.cloudpnr.com (测试)'; $output->writeln('后台 env: ' . $env); $output->writeln('SDK PROD_MODE: ' . $prodMode . ' -> ' . $gateway); $output->writeln('小程序 AppID: ' . ($appId ?: '(空)')); $output->writeln('商户号 huifu_id: ' . $config->get('huifu_id')); $output->writeln('sys_id: ' . $config->get('sys_id')); $output->writeln('product_id: ' . $config->get('product_id')); $payScene = trim((string) $config->get('pay_scene')); $channelNo = trim((string) $config->get('channel_no')); $output->writeln('pay_scene: ' . ($payScene ?: '(空)') . ' (01=标准费率线上,02=标准费率线下)'); $output->writeln('channel_no: ' . ($channelNo ?: '(空)')); if ($payScene === '02') { $output->writeln('[WARN] pay_scene=02 表示标准费率线下;若控台是「标准费率线上」应改为 01 或留空'); } $output->writeln('延时分账 delay_acct: ' . ($config->shouldDelayAcct() ? 'Y' : 'N')); if ($env === 'test' && $prodMode === 'true') { $output->writeln('[WARN] 后台选了测试环境,但 SDK 仍走生产网关,请更新 SdkLoader.php'); } if ($env === 'prod' && $prodMode === 'false') { $output->writeln('[WARN] 后台选了生产环境,但 SDK 走测试网关,请更新 SdkLoader.php'); } $output->writeln(''); $output->writeln('若报「汇付未返回 pay_info」,请核对:'); $output->writeln(' 1) env 与汇付商户凭证是否同一环境(测试/生产不能混用)'); $output->writeln(' 2) 小程序 AppID 与 sub_openid 是否同一应用'); $output->writeln(' 3) 汇付控台是否已开通 T_MINIAPP 微信小程序通道'); $output->writeln(' 4) 查 runtime/log 中 [shopro_huifu] missing-pay_info 日志'); } }