'test_app_id', 'merc_no' => 'test_merc_no', 'private_key' => 'tests/mock/cert/merchant_private_key.pem', 'huawei_public_key' => 'tests/mock/cert/huawei_public_key.pem', 'sandbox' => true ]; /** * 运行所有测试 */ public function runTests() { echo "开始测试华为支付客户端...\n"; try { $this->testConstructSuccess(); $this->testConstructMissingConfig(); $this->testGenerateSign(); $this->testH5Pay(); $this->testOrderQuery(); $this->testRefund(); $this->testVerifySign(); echo "\n✅ 所有测试通过!\n"; return true; } catch (\Exception $e) { echo "\n❌ 测试失败:" . $e->getMessage() . "\n"; echo "错误位置:" . $e->getFile() . " 第" . $e->getLine() . "行\n"; return false; } } /** * 测试构造函数 - 成功场景 */ public function testConstructSuccess() { echo "测试构造函数成功场景..."; // 模拟证书文件 $this->createMockCertFiles(); try { $client = new HuaweiPayClient($this->testConfig); echo " ✅ 通过\n"; } catch (\Exception $e) { throw new \Exception("构造函数测试失败: " . $e->getMessage()); } } /** * 测试构造函数 - 缺少必要配置 */ public function testConstructMissingConfig() { echo "测试构造函数缺少配置..."; // 测试缺少app_id try { $config = $this->testConfig; unset($config['app_id']); new HuaweiPayClient($config); throw new \Exception("应该抛出缺少app_id的异常"); } catch (\Exception $e) { if (strpos($e->getMessage(), '缺少必要配置:app_id') === false) { throw new \Exception("异常信息不正确: " . $e->getMessage()); } } echo " ✅ 通过\n"; } /** * 测试生成签名 */ public function testGenerateSign() { echo "测试生成签名..."; $this->createMockCertFiles(); $client = new HuaweiPayClient($this->testConfig); // 使用反射调用私有方法 $reflection = new \ReflectionClass($client); $method = $reflection->getMethod('generateSign'); $method->setAccessible(true); $params = [ 'app_id' => 'test_app_id', 'merc_no' => 'test_merc_no', 'order_id' => 'test_order_001', 'amount' => 100, 'sign' => 'ignored' ]; // 测试签名不应该失败 try { $sign = $method->invoke($client, $params); $this->assertEquals(true, is_string($sign), "签名应该是字符串"); echo " ✅ 通过\n"; } catch (\Exception $e) { throw new \Exception("签名生成测试失败: " . $e->getMessage()); } } /** * 测试H5支付 */ public function testH5Pay() { echo "测试H5支付..."; $this->createMockCertFiles(); // 模拟HTTP请求 $this->mockHttpRequest(); $client = new HuaweiPayClient($this->testConfig); $params = [ 'order_id' => 'test_order_001', 'amount' => 100, 'subject' => '测试商品', 'notify_url' => 'https://example.com/notify', 'return_url' => 'https://example.com/return' ]; // 由于无法真正发送HTTP请求,这里只测试参数验证 try { // 尝试调用,实际环境可能需要模拟curl $result = $client->h5Pay($params); echo " ⚠️ 跳过实际HTTP请求测试\n"; } catch (\Exception $e) { // 预期可能会有curl错误,这里只检查是否因为参数问题 if (strpos($e->getMessage(), '缺少必要参数') !== false) { throw new \Exception("H5支付参数测试失败: " . $e->getMessage()); } echo " ⚠️ 跳过实际HTTP请求测试\n"; } } /** * 测试订单查询 */ public function testOrderQuery() { echo "测试订单查询..."; $this->createMockCertFiles(); $client = new HuaweiPayClient($this->testConfig); $params = [ 'order_id' => 'test_order_001' ]; try { // 尝试调用,实际环境可能需要模拟curl $result = $client->orderQuery($params); echo " ⚠️ 跳过实际HTTP请求测试\n"; } catch (\Exception $e) { if (strpos($e->getMessage(), '缺少必要参数') !== false) { throw new \Exception("订单查询参数测试失败: " . $e->getMessage()); } echo " ⚠️ 跳过实际HTTP请求测试\n"; } } /** * 测试退款 */ public function testRefund() { echo "测试退款..."; $this->createMockCertFiles(); $client = new HuaweiPayClient($this->testConfig); $params = [ 'order_id' => 'test_order_001', 'refund_order_id' => 'refund_001', 'refund_amount' => 100 ]; try { // 尝试调用,实际环境可能需要模拟curl $result = $client->refund($params); echo " ⚠️ 跳过实际HTTP请求测试\n"; } catch (\Exception $e) { if (strpos($e->getMessage(), '缺少必要参数') !== false) { throw new \Exception("退款参数测试失败: " . $e->getMessage()); } echo " ⚠️ 跳过实际HTTP请求测试\n"; } } /** * 测试签名验证 */ public function testVerifySign() { echo "测试签名验证..."; $this->createMockCertFiles(); $client = new HuaweiPayClient($this->testConfig); // 使用反射调用私有方法 $reflection = new \ReflectionClass($client); $method = $reflection->getMethod('verifySign'); $method->setAccessible(true); $params = [ 'app_id' => 'test_app_id', 'merc_no' => 'test_merc_no', 'order_id' => 'test_order_001', 'amount' => 100 ]; // 由于无法真正验证签名,这里只测试方法是否存在 try { // 传入空签名,预期会失败 $verified = $method->invoke($client, $params, ''); echo " ⚠️ 跳过实际签名验证测试\n"; } catch (\Exception $e) { echo " ⚠️ 跳过实际签名验证测试\n"; } } /** * 创建模拟证书文件 */ private function createMockCertFiles() { // 确保mock证书目录存在 $certDir = __DIR__ . '/mock/cert/'; if (!is_dir($certDir)) { mkdir($certDir, 0755, true); } // 创建模拟证书文件 $privateKeyPath = $certDir . 'merchant_private_key.pem'; $publicKeyPath = $certDir . 'huawei_public_key.pem'; if (!file_exists($privateKeyPath)) { file_put_contents($privateKeyPath, '-----BEGIN PRIVATE KEY-----\nMOCK_PRIVATE_KEY_CONTENT\n-----END PRIVATE KEY-----'); } if (!file_exists($publicKeyPath)) { file_put_contents($publicKeyPath, '-----BEGIN PUBLIC KEY-----\nMOCK_PUBLIC_KEY_CONTENT\n-----END PUBLIC KEY-----'); } } /** * 模拟HTTP请求 */ private function mockHttpRequest() { // 在实际测试中,可以使用更高级的模拟技术 // 这里仅提供基础框架 } /** * 断言相等 */ private function assertEquals($expected, $actual, $message = '') { if ($expected !== $actual) { throw new \Exception($message ?: "断言失败: 期望 {$expected},实际得到 {$actual}"); } } } // 如果直接运行此文件,则执行测试 if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) { $test = new HuaweiPayClientTest(); $test->runTests(); }