diff --git a/src/addon/huaweipay/tests/HuaweiPayClientTest.php b/src/addon/huaweipay/tests/HuaweiPayClientTest.php
deleted file mode 100644
index 548fbbf1b..000000000
--- a/src/addon/huaweipay/tests/HuaweiPayClientTest.php
+++ /dev/null
@@ -1,263 +0,0 @@
-configData = yaml_parse_file($yamlPath);
- } else {
- // 如果没有yaml_parse_file函数,手动解析
- $this->configData = $this->parseYamlFile($yamlPath);
- }
- } else {
- throw new Exception("配置文件不存在: {$yamlPath}");
- }
- }
-
- /**
- * 手动解析YAML文件
- * @param string $filePath
- * @return array
- */
- private function parseYamlFile($filePath)
- {
- $content = file_get_contents($filePath);
- $lines = explode("\n", $content);
- $result = [];
- $currentKey = '';
- $isMultiline = false;
- $multilineValue = '';
-
- foreach ($lines as $line) {
- $line = trim($line);
-
- // 跳过注释和空行
- if (empty($line) || strpos($line, '#') === 0) {
- continue;
- }
-
- // 处理多行值
- if ($isMultiline) {
- if (strpos($line, '-----END') === 0) {
- $multilineValue .= $line . "\n";
- $result[$currentKey] = rtrim($multilineValue);
- $isMultiline = false;
- $multilineValue = '';
- } else {
- $multilineValue .= $line . "\n";
- }
- continue;
- }
-
- // 处理单行键值对
- if (strpos($line, ':') !== false) {
- list($key, $value) = explode(':', $line, 2);
- $key = trim($key);
- $value = trim($value);
-
- // 处理多行值开始
- if ($value === '|') {
- $isMultiline = true;
- $currentKey = $key;
- continue;
- }
-
- // 处理普通值
- if (!empty($value)) {
- $result[$key] = $value;
- }
- }
- }
-
- return $result;
- }
-
- /**
- * 模拟证书内容格式化方法
- * 与HuaweiPayClient::formatCertificateContent方法实现一致
- */
- private function formatCertificateContent($content, $type)
- {
- // 移除空白字符和换行
- $content = preg_replace('/\s+/', '', $content);
-
- // 检查是否已经包含格式头尾
- if (preg_match('/-----BEGIN\s+.*?-----/', $content) && preg_match('/-----END\s+.*?-----/', $content)) {
- return $content;
- }
-
- // 添加适当的格式头尾
- $header = '';
- $footer = '';
-
- if ($type == 'private_key') {
- $header = "-----BEGIN PRIVATE KEY-----";
- $footer = "-----END PRIVATE KEY-----";
- } elseif ($type == 'public_key') {
- $header = "-----BEGIN PUBLIC KEY-----";
- $footer = "-----END PUBLIC KEY-----";
- }
-
- // 移除可能存在的旧格式
- $content = preg_replace('/-----BEGIN.*?-----/', '', $content);
- $content = preg_replace('/-----END.*?-----/', '', $content);
-
- // 组合最终格式
- return $header . $content . $footer;
- }
-
- /**
- * 测试证书内容格式化功能
- */
- public function testFormatCertificateContent()
- {
- echo "开始测试证书内容格式化功能...\n\n";
-
- // 从配置文件获取实际的密钥内容
- $privateKey = $this->configData['privateKey'];
- $publicKey = $this->configData['huawei_public_key'];
-
- // 测试1:私钥格式化
- echo "测试1:私钥格式化...";
- $formattedPrivateKey = $this->formatCertificateContent($privateKey, 'private_key');
-
- // 移除预期结果和实际结果中的所有换行符,统一比较
- $expectedClean = str_replace("\n", '', $privateKey);
- $actualClean = str_replace("\n", '', $formattedPrivateKey);
-
- if (strpos($actualClean, '-----BEGIN PRIVATE KEY-----') !== false && strpos($actualClean, '-----END PRIVATE KEY-----') !== false) {
- echo " ✅ 通过\n";
- } else {
- echo " ❌ 失败\n";
- echo " 实际:$formattedPrivateKey\n";
- return false;
- }
-
- // 测试2:公钥格式化
- echo "测试2:公钥格式化...";
- $formattedPublicKey = $this->formatCertificateContent($publicKey, 'public_key');
-
- // 移除预期结果和实际结果中的所有换行符,统一比较
- $expectedClean = str_replace("\n", '', $publicKey);
- $actualClean = str_replace("\n", '', $formattedPublicKey);
-
- if (strpos($actualClean, '-----BEGIN PUBLIC KEY-----') !== false && strpos($actualClean, '-----END PUBLIC KEY-----') !== false) {
- echo " ✅ 通过\n";
- } else {
- echo " ❌ 失败\n";
- echo " 实际:$formattedPublicKey\n";
- return false;
- }
-
- // 测试3:带空格和换行的私钥格式化
- echo "测试3:带空格和换行的私钥格式化...";
- $privateKeyWithSpaces = ' ' . $privateKey . ' ';
- $formattedPrivateKeyWithSpaces = $this->formatCertificateContent($privateKeyWithSpaces, 'private_key');
-
- if (strpos($formattedPrivateKeyWithSpaces, '-----BEGIN PRIVATE KEY-----') !== false && strpos($formattedPrivateKeyWithSpaces, '-----END PRIVATE KEY-----') !== false) {
- echo " ✅ 通过\n";
- } else {
- echo " ❌ 失败\n";
- echo " 实际:$formattedPrivateKeyWithSpaces\n";
- return false;
- }
-
- echo "\n✅ 所有证书内容格式化测试通过!\n";
- return true;
- }
-
- /**
- * 测试文本模式配置结构
- */
- public function testTextModeConfigStructure()
- {
- echo "\n开始测试文本模式配置结构...\n";
-
- // 测试1:基本文本模式配置
- echo "测试1:基本文本模式配置...";
- $config1 = [
- 'app_id' => $this->configData['app_id'],
- 'mch_id' => $this->configData['mch_id'],
- 'private_key_text' => $this->configData['privateKey'],
- 'huawei_public_key_text' => $this->configData['huawei_public_key']
- ];
-
- if (isset($config1['private_key_text']) && isset($config1['huawei_public_key_text'])) {
- echo " ✅ 通过\n";
- } else {
- echo " ❌ 失败\n";
- return false;
- }
-
- // 测试2:验证配置数据完整性
- echo "测试2:验证配置数据完整性...";
- $requiredFields = ['app_id', 'mch_id', 'mch_auth_id', 'privateKey', 'huawei_public_key'];
- $missingFields = [];
-
- foreach ($requiredFields as $field) {
- if (!isset($this->configData[$field])) {
- $missingFields[] = $field;
- }
- }
-
- if (empty($missingFields)) {
- echo " ✅ 通过\n";
- } else {
- echo " ❌ 失败,缺少字段:" . implode(', ', $missingFields) . "\n";
- return false;
- }
-
- echo "\n✅ 所有文本模式配置结构测试通过!\n";
- return true;
- }
-
- /**
- * 运行所有测试
- */
- public function runTests()
- {
- echo "开始运行华为支付文本模式测试...\n\n";
-
- try {
- $formatResult = $this->testFormatCertificateContent();
- $configResult = $this->testTextModeConfigStructure();
-
- if ($formatResult && $configResult) {
- echo "\n🎉 所有测试通过!华为支付文本模式功能正常工作!\n";
- return true;
- } else {
- echo "\n💥 部分测试失败!\n";
- return false;
- }
- } catch (Exception $e) {
- echo "\n💥 测试失败:" . $e->getMessage() . "\n";
- echo "错误位置:" . $e->getFile() . " 第" . $e->getLine() . "行\n";
- return false;
- }
- }
-}
-
-// 如果直接运行此文件,则执行测试
-if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) {
- $test = new CertificateFormatTest();
- $test->runTests();
-}
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/HuaweiPayConfigTest.php b/src/addon/huaweipay/tests/HuaweiPayConfigTest.php
new file mode 100644
index 000000000..03b43e6a5
--- /dev/null
+++ b/src/addon/huaweipay/tests/HuaweiPayConfigTest.php
@@ -0,0 +1,191 @@
+configData['private_key_text'];
+ $publicKey = $this->configData['huawei_public_key_text'];
+
+ // 测试1:私钥格式化(文本内容模式)
+ $formattedPrivateKey = $this->formatCertificateContent($privateKey, 'private_key');
+ $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $formattedPrivateKey, '私钥格式化失败:缺少BEGIN标记');
+ $this->assertStringContainsString('-----END PRIVATE KEY-----', $formattedPrivateKey, '私钥格式化失败:缺少END标记');
+
+ // 测试2:公钥格式化(文本内容模式)
+ $formattedPublicKey = $this->formatCertificateContent($publicKey, 'public_key');
+ $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $formattedPublicKey, '公钥格式化失败:缺少BEGIN标记');
+ $this->assertStringContainsString('-----END PUBLIC KEY-----', $formattedPublicKey, '公钥格式化失败:缺少END标记');
+
+ // 测试3:带空格和换行的私钥格式化
+ $privateKeyWithSpaces = ' ' . $privateKey . ' ';
+ $formattedPrivateKeyWithSpaces = $this->formatCertificateContent($privateKeyWithSpaces, 'private_key');
+ $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $formattedPrivateKeyWithSpaces, '带空格私钥格式化失败:缺少BEGIN标记');
+ $this->assertStringContainsString('-----END PRIVATE KEY-----', $formattedPrivateKeyWithSpaces, '带空格私钥格式化失败:缺少END标记');
+
+ // 测试4:没有格式的私钥格式化
+ $rawPrivateKey = str_replace(["\n", '-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----'], '', $privateKey);
+ $formattedRawPrivateKey = $this->formatCertificateContent($rawPrivateKey, 'private_key');
+ $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $formattedRawPrivateKey, '无格式私钥格式化失败:缺少BEGIN标记');
+ $this->assertStringContainsString('-----END PRIVATE KEY-----', $formattedRawPrivateKey, '无格式私钥格式化失败:缺少END标记');
+ }
+
+ /**
+ * 测试文本模式配置结构
+ */
+ public function testTextModeConfigStructure()
+ {
+ // 测试1:基本文本模式配置
+ $config = [
+ 'app_id' => $this->configData['app_id'],
+ 'merc_no' => $this->configData['merc_no'],
+ 'private_key_text' => $this->configData['private_key_text'],
+ 'huawei_public_key_text' => $this->configData['huawei_public_key_text']
+ ];
+
+ $this->assertArrayHasKey('private_key_text', $config, '配置缺少private_key_text字段');
+ $this->assertArrayHasKey('huawei_public_key_text', $config, '配置缺少huawei_public_key_text字段');
+
+ // 测试2:验证配置数据完整性
+ $requiredFields = ['app_id', 'merc_no', 'mch_auth_id', 'private_key_text', 'huawei_public_key_text'];
+ foreach ($requiredFields as $field) {
+ $this->assertArrayHasKey($field, $this->configData, "配置文件缺少必填字段:{$field}");
+ }
+ }
+
+ /**
+ * 测试文件路径模式配置
+ */
+ public function testFilePathModeConfig()
+ {
+ // 测试1:检查文件路径配置是否存在
+ $this->assertArrayHasKey('private_key', $this->configData, '配置缺少private_key字段(文件路径模式)');
+ $this->assertArrayHasKey('huawei_public_key', $this->configData, '配置缺少huawei_public_key字段(文件路径模式)');
+
+ // 测试2:验证文件路径格式
+ $privateKeyPath = $this->configData['private_key'];
+ $publicKeyPath = $this->configData['huawei_public_key'];
+
+ $this->assertStringEndsWith('.pem', $privateKeyPath, '私钥文件路径必须以.pem结尾');
+ $this->assertStringEndsWith('.pem', $publicKeyPath, '公钥文件路径必须以.pem结尾');
+
+ // 测试3:验证文件是否存在(如果在测试环境中可用)
+ $fullPrivateKeyPath = __DIR__ . '/' . $privateKeyPath;
+ $fullPublicKeyPath = __DIR__ . '/' . $publicKeyPath;
+
+ if (file_exists($fullPrivateKeyPath)) {
+ $this->assertFileExists($fullPrivateKeyPath, '私钥文件不存在');
+ }
+
+ if (file_exists($fullPublicKeyPath)) {
+ $this->assertFileExists($fullPublicKeyPath, '公钥文件不存在');
+ }
+
+ // 测试4:验证文件内容(如果文件存在)
+ if (file_exists($fullPrivateKeyPath)) {
+ $privateKeyContent = file_get_contents($fullPrivateKeyPath);
+ $this->assertNotEmpty($privateKeyContent, '私钥文件内容为空');
+ $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $privateKeyContent, '私钥文件内容格式不正确');
+ $this->assertStringContainsString('-----END PRIVATE KEY-----', $privateKeyContent, '私钥文件内容格式不正确');
+ }
+
+ if (file_exists($fullPublicKeyPath)) {
+ $publicKeyContent = file_get_contents($fullPublicKeyPath);
+ $this->assertNotEmpty($publicKeyContent, '公钥文件内容为空');
+ $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $publicKeyContent, '公钥文件内容格式不正确');
+ $this->assertStringContainsString('-----END PUBLIC KEY-----', $publicKeyContent, '公钥文件内容格式不正确');
+ }
+ }
+
+ /**
+ * 测试两种配置模式的相互转换
+ */
+ public function testConfigModeConversion()
+ {
+ // 测试1:文本内容转换为文件路径模式(模拟)
+ $privateKeyText = $this->configData['private_key_text'];
+ $publicKeyText = $this->configData['huawei_public_key_text'];
+
+ // 模拟将文本内容保存到临时文件
+ $tempPrivateKeyPath = sys_get_temp_dir() . '/temp_private_key.pem';
+ $tempPublicKeyPath = sys_get_temp_dir() . '/temp_public_key.pem';
+
+ file_put_contents($tempPrivateKeyPath, $privateKeyText);
+ file_put_contents($tempPublicKeyPath, $publicKeyText);
+
+ // 验证临时文件内容
+ $this->assertFileExists($tempPrivateKeyPath, '临时私钥文件创建失败');
+ $this->assertFileExists($tempPublicKeyPath, '临时公钥文件创建失败');
+
+ $this->assertStringEqualsFile($tempPrivateKeyPath, $privateKeyText, '临时私钥文件内容与原文本不一致');
+ $this->assertStringEqualsFile($tempPublicKeyPath, $publicKeyText, '临时公钥文件内容与原文本不一致');
+
+ // 清理临时文件
+ unlink($tempPrivateKeyPath);
+ unlink($tempPublicKeyPath);
+
+ // 测试2:文件路径转换为文本内容模式(如果文件存在)
+ $privateKeyPath = $this->configData['private_key'];
+ $publicKeyPath = $this->configData['huawei_public_key'];
+
+ $fullPrivateKeyPath = __DIR__ . '/' . $privateKeyPath;
+ $fullPublicKeyPath = __DIR__ . '/' . $publicKeyPath;
+
+ if (file_exists($fullPrivateKeyPath) && file_exists($fullPublicKeyPath)) {
+ $privateKeyContent = file_get_contents($fullPrivateKeyPath);
+ $publicKeyContent = file_get_contents($fullPublicKeyPath);
+
+ // 验证从文件读取的内容可以被正确格式化
+ $formattedPrivateKey = $this->formatCertificateContent($privateKeyContent, 'private_key');
+ $formattedPublicKey = $this->formatCertificateContent($publicKeyContent, 'public_key');
+
+ $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $formattedPrivateKey, '从文件读取的私钥格式化失败');
+ $this->assertStringContainsString('-----END PRIVATE KEY-----', $formattedPrivateKey, '从文件读取的私钥格式化失败');
+ $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $formattedPublicKey, '从文件读取的公钥格式化失败');
+ $this->assertStringContainsString('-----END PUBLIC KEY-----', $formattedPublicKey, '从文件读取的公钥格式化失败');
+ }
+ }
+
+ /**
+ * 测试配置模式优先级
+ */
+ public function testConfigModePriority()
+ {
+ // 测试1:文本内容模式优先于文件路径模式
+ // 模拟配置同时包含两种模式
+ $config = [
+ 'private_key' => 'mock/cert/merchant_private_key.pem',
+ 'private_key_text' => $this->configData['private_key_text'],
+ 'huawei_public_key' => 'mock/cert/huawei_public_key.pem',
+ 'huawei_public_key_text' => $this->configData['huawei_public_key_text']
+ ];
+
+ // 验证两种模式的配置都存在
+ $this->assertArrayHasKey('private_key', $config, '配置缺少private_key字段');
+ $this->assertArrayHasKey('private_key_text', $config, '配置缺少private_key_text字段');
+ $this->assertArrayHasKey('huawei_public_key', $config, '配置缺少huawei_public_key字段');
+ $this->assertArrayHasKey('huawei_public_key_text', $config, '配置缺少huawei_public_key_text字段');
+
+ // 模拟应用文本内容优先的逻辑
+ $finalPrivateKey = isset($config['private_key_text']) ? $config['private_key_text'] : $config['private_key'];
+ $finalPublicKey = isset($config['huawei_public_key_text']) ? $config['huawei_public_key_text'] : $config['huawei_public_key'];
+
+ // 验证文本内容模式被优先使用
+ $this->assertSame($config['private_key_text'], $finalPrivateKey, '文本内容模式应该优先于文件路径模式');
+ $this->assertSame($config['huawei_public_key_text'], $finalPublicKey, '文本内容模式应该优先于文件路径模式');
+ }
+}
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/PayModelTest.php b/src/addon/huaweipay/tests/PayModelTest.php
index ce4fe19b5..1af59b64d 100644
--- a/src/addon/huaweipay/tests/PayModelTest.php
+++ b/src/addon/huaweipay/tests/PayModelTest.php
@@ -3,263 +3,417 @@
* 华为支付模型测试类
*/
-namespace addon\huaweipay\tests;
+// 设置错误报告
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
-use addon\huaweipay\model\Pay;
-use addon\huaweipay\model\Config;
-use think\facade\Log;
+// 添加自动加载路径
+$root_path = dirname(__DIR__, 3);
+require_once $root_path . '/vendor/autoload.php';
+
+// 定义常量
+if (!defined('ROOT_PATH')) {
+ define('ROOT_PATH', $root_path . '/');
+}
+
+// 模拟app()函数,解决框架依赖问题
+function app() {
+ return new class {
+ public function getRootPath() {
+ return dirname(__DIR__, 3) . '\\';
+ }
+ };
+}
class PayModelTest
{
- /**
- * 测试站点ID
- * @var int
- */
- protected $siteId = 1;
-
- /**
- * 测试配置数据
- * @var array
- */
- protected $configData;
+ protected $config = [];
+ protected $pay_model = null;
/**
- * 构造函数,加载配置文件
+ * 构造函数
*/
public function __construct()
{
- // 加载配置文件
- $yamlPath = __DIR__ . '/mock/data.yml';
- if (file_exists($yamlPath)) {
- if (function_exists('yaml_parse_file')) {
- $this->configData = yaml_parse_file($yamlPath);
- } else {
- // 如果没有yaml_parse_file函数,手动解析
- $this->configData = $this->parseYamlFile($yamlPath);
- }
- } else {
- throw new Exception("配置文件不存在: {$yamlPath}");
- }
+ // 加载测试配置
+ $this->config = $this->parseYamlFile(__DIR__ . '/mock/data.yml');
+ echo "配置加载完成\n";
+ // print_r($this->config);
}
-
+
/**
- * 手动解析YAML文件
- * @param string $filePath
+ * 解析YAML文件
+ * @param string $file_path YAML文件路径
* @return array
*/
- private function parseYamlFile($filePath)
+ protected function parseYamlFile($file_path)
{
- $content = file_get_contents($filePath);
- $lines = explode("\n", $content);
- $result = [];
- $currentKey = '';
- $isMultiline = false;
- $multilineValue = '';
+ if (!file_exists($file_path)) {
+ throw new Exception("YAML文件不存在: {$file_path}");
+ }
+
+ $yaml_content = file_get_contents($file_path);
+ if ($yaml_content === false) {
+ throw new Exception("读取YAML文件失败: {$file_path}");
+ }
+
+ // 使用正则替换YAML中的\n为实际换行符
+ $yaml_content = preg_replace_callback('/\\\\n/', function($matches) {
+ return "\n";
+ }, $yaml_content);
+
+ // 简单的YAML解析(仅支持当前测试文件的格式)
+ $config = [];
+ $lines = explode("\n", $yaml_content);
+ $current_key = '';
+ $in_block = false;
+ $block_content = '';
foreach ($lines as $line) {
$line = trim($line);
-
- // 跳过注释和空行
if (empty($line) || strpos($line, '#') === 0) {
continue;
}
-
- // 处理多行值
- if ($isMultiline) {
- if (strpos($line, '-----END') === 0) {
- $multilineValue .= $line . "\n";
- $result[$currentKey] = rtrim($multilineValue);
- $isMultiline = false;
- $multilineValue = '';
- } else {
- $multilineValue .= $line . "\n";
+
+ if ($in_block) {
+ if (strpos($line, '---') === 0) {
+ continue;
+ }
+ if (preg_match('/^\w+:/', $line)) {
+ $config[$current_key] = $block_content;
+ $in_block = false;
+ $block_content = '';
+ } else {
+ $block_content .= $line . "\n";
+ continue;
}
- continue;
}
-
- // 处理单行键值对
+
if (strpos($line, ':') !== false) {
list($key, $value) = explode(':', $line, 2);
$key = trim($key);
$value = trim($value);
- // 处理多行值开始
- if ($value === '|') {
- $isMultiline = true;
- $currentKey = $key;
+ if (strpos($value, '|') !== false) {
+ $in_block = true;
+ $current_key = $key;
continue;
}
- // 处理普通值
if (!empty($value)) {
- $result[$key] = $value;
+ $config[$key] = $value;
}
}
}
- return $result;
+ if ($in_block && !empty($current_key)) {
+ $config[$current_key] = $block_content;
+ }
+
+ return $config;
}
/**
- * 运行所有测试
+ * 运行测试
*/
public function runTests()
{
- echo "开始测试华为支付模型...\n";
+ echo "开始运行华为支付模型测试...\n";
try {
+ // 1. 测试支付方法
+ echo "1. 测试支付方法...\n";
$this->testPayMethod();
- $this->testNotifyMethod();
- $this->testCloseOrderMethod();
- $this->testRefundMethod();
- $this->testQueryOrderMethod();
+ echo "1. 支付方法测试通过\n";
- echo "\n✅ 所有测试通过!\n";
- return true;
- } catch (\Exception $e) {
- echo "\n❌ 测试失败:" . $e->getMessage() . "\n";
- echo "错误位置:" . $e->getFile() . " 第" . $e->getLine() . "行\n";
+ // 2. 测试关闭订单方法
+ echo "2. 测试关闭订单方法...\n";
+ $this->testCloseOrderMethod();
+ echo "2. 关闭订单方法测试通过\n";
+
+ // 3. 测试退款方法
+ echo "3. 测试退款方法...\n";
+ $this->testRefundMethod();
+ echo "3. 退款方法测试通过\n";
+
+ // 4. 测试查询订单方法
+ echo "4. 测试查询订单方法...\n";
+ $this->testQueryOrderMethod();
+ echo "4. 查询订单方法测试通过\n";
+
+ // 5. 测试回调验证方法
+ echo "5. 测试回调验证方法...\n";
+ $this->testCallbackVerification();
+ echo "5. 回调验证方法测试通过\n";
+
+ echo "\n所有测试通过!\n";
+ } catch (Exception $e) {
+ echo "\n测试失败:" . $e->getMessage() . "\n";
+ echo "错误堆栈:" . $e->getTraceAsString() . "\n";
return false;
}
+
+ return true;
}
/**
* 测试支付方法
*/
- public function testPayMethod()
+ protected function testPayMethod()
{
- echo "测试支付方法...";
+ // 创建支付模型实例
+ require_once dirname(__DIR__) . '/model/Pay.php';
+ require_once dirname(__DIR__) . '/data/sdk/utils.php';
+ require_once dirname(__DIR__) . '/data/sdk/HuaweiPayClient.php';
+
+ // 添加必要的配置
+ $site_id = '1';
+ $config_data = $this->config;
+ $config_data['site_id'] = $site_id;
+ // 使用文本模式的密钥,而不是证书文件
+ unset($config_data['private_key']);
+ unset($config_data['huawei_public_key']);
try {
- // 模拟配置
- $this->mockConfig();
+ // 创建实际的华为支付客户端实例
+ $project_root = dirname(__DIR__, 4); // 获取项目根目录
+ $huawei_pay_client = new \addon\huaweipay\data\sdk\HuaweiPayClient($config_data, $project_root);
+ echo " - 华为支付客户端实例创建成功\n";
- // 创建测试订单数据
- $orderInfo = [
- 'out_trade_no' => 'test_order_' . time(),
- 'total_amount' => 100,
- 'subject' => '测试商品',
- 'notify_url' => $this->configData['notifyUrl'],
- 'return_url' => $this->configData['returnUrl'],
- 'buyer_id' => '1',
- 'type' => 'h5'
+ // 测试H5支付
+ $h5_params = [
+ 'out_trade_no' => 'TEST_H5_' . time(),
+ 'subject' => '测试H5支付商品',
+ 'body' => '测试H5支付商品描述',
+ 'total_amount' => 0.01,
+ 'client_ip' => '127.0.0.1'
];
- // 尝试创建Pay实例
- $payModel = new Pay($this->siteId);
+ // 调用真实的H5支付方法
+ $h5_result = $huawei_pay_client->h5Pay($h5_params, $config_data['returnUrl'], $config_data['notifyUrl']);
+ echo " - H5支付方法调用成功\n";
+ echo " 结果: " . json_encode($h5_result, JSON_UNESCAPED_UNICODE) . "\n";
- // 由于需要实际的支付客户端,这里只测试模型初始化
- echo " ⚠️ 跳过实际支付测试,仅测试模型初始化\n";
- } catch (\Exception $e) {
- echo " ⚠️ 模型初始化测试跳过: " . $e->getMessage() . "\n";
- }
- }
-
- /**
- * 测试通知方法
- */
- public function testNotifyMethod()
- {
- echo "测试通知方法...";
-
- try {
- // 模拟配置
- $this->mockConfig();
-
- // 创建模拟通知数据
- $notifyData = [
- 'app_id' => $this->configData['app_id'],
- 'merc_no' => $this->configData['mch_id'],
- 'order_id' => 'test_order_001',
- 'trade_status' => 'SUCCESS',
- 'sign' => 'mock_signature'
+ // 测试APP支付
+ $app_params = [
+ 'out_trade_no' => 'TEST_APP_' . time(),
+ 'subject' => '测试APP支付商品',
+ 'body' => '测试APP支付商品描述',
+ 'total_amount' => 0.01
];
- // 尝试创建Pay实例
- $payModel = new Pay($this->siteId);
+ // 调用真实的APP支付方法
+ $app_result = $huawei_pay_client->appPay($app_params, $config_data['notifyUrl']);
+ echo " - APP支付方法调用成功\n";
+ echo " 结果: " . json_encode($app_result, JSON_UNESCAPED_UNICODE) . "\n";
- echo " ⚠️ 跳过实际通知处理测试\n";
- } catch (\Exception $e) {
- echo " ⚠️ 通知测试跳过: " . $e->getMessage() . "\n";
+ } catch (Exception $e) {
+ echo " - 支付测试失败: " . $e->getMessage() . "\n";
+ throw $e;
}
}
/**
* 测试关闭订单方法
*/
- public function testCloseOrderMethod()
+ protected function testCloseOrderMethod()
{
- echo "测试关闭订单方法...";
+ require_once dirname(__DIR__) . '/data/sdk/utils.php';
+ require_once dirname(__DIR__) . '/data/sdk/HuaweiPayClient.php';
+
+ // 添加必要的配置
+ $site_id = '1';
+ $config_data = $this->config;
+ $config_data['site_id'] = $site_id;
+ // 使用文本模式的密钥,而不是证书文件
+ unset($config_data['private_key']);
+ unset($config_data['huawei_public_key']);
try {
- // 模拟配置
- $this->mockConfig();
+ // 创建实际的华为支付客户端实例
+ $project_root = dirname(__DIR__, 4); // 获取项目根目录
+ $huawei_pay_client = new \addon\huaweipay\data\sdk\HuaweiPayClient($config_data, $project_root);
+ echo " - 华为支付客户端实例创建成功\n";
- // 尝试创建Pay实例
- $payModel = new Pay($this->siteId);
+ // 测试关闭订单
+ $params = [
+ 'out_trade_no' => 'TEST_CLOSE_' . time()
+ ];
- echo " ⚠️ 跳过实际关闭订单测试\n";
- } catch (\Exception $e) {
- echo " ⚠️ 关闭订单测试跳过: " . $e->getMessage() . "\n";
+ // 调用真实的关闭订单方法
+ $close_result = $huawei_pay_client->closeOrder($params);
+ echo " - 关闭订单方法调用成功\n";
+ echo " 结果: " . json_encode($close_result, JSON_UNESCAPED_UNICODE) . "\n";
+
+ } catch (Exception $e) {
+ echo " - 关闭订单测试失败: " . $e->getMessage() . "\n";
+ throw $e;
}
}
/**
* 测试退款方法
*/
- public function testRefundMethod()
+ protected function testRefundMethod()
{
- echo "测试退款方法...";
+ require_once dirname(__DIR__) . '/data/sdk/utils.php';
+ require_once dirname(__DIR__) . '/data/sdk/HuaweiPayClient.php';
+
+ // 添加必要的配置
+ $site_id = '1';
+ $config_data = $this->config;
+ $config_data['site_id'] = $site_id;
+ // 使用文本模式的密钥,而不是证书文件
+ unset($config_data['private_key']);
+ unset($config_data['huawei_public_key']);
try {
- // 模拟配置
- $this->mockConfig();
+ // 创建实际的华为支付客户端实例
+ $project_root = dirname(__DIR__, 4); // 获取项目根目录
+ $huawei_pay_client = new \addon\huaweipay\data\sdk\HuaweiPayClient($config_data, $project_root);
+ echo " - 华为支付客户端实例创建成功\n";
- // 尝试创建Pay实例
- $payModel = new Pay($this->siteId);
+ // 测试退款
+ $params = [
+ 'out_trade_no' => 'TEST_REFUND_' . time(),
+ 'out_request_no' => 'REFUND_' . time(),
+ 'refund_amount' => 0.01,
+ 'refund_reason' => '测试退款'
+ ];
- echo " ⚠️ 跳过实际退款测试\n";
- } catch (\Exception $e) {
- echo " ⚠️ 退款测试跳过: " . $e->getMessage() . "\n";
+ // 调用真实的退款方法
+ $refund_result = $huawei_pay_client->refund($params);
+ echo " - 退款方法调用成功\n";
+ echo " 结果: " . json_encode($refund_result, JSON_UNESCAPED_UNICODE) . "\n";
+
+ } catch (Exception $e) {
+ echo " - 退款测试失败: " . $e->getMessage() . "\n";
+ throw $e;
}
}
/**
* 测试查询订单方法
*/
- public function testQueryOrderMethod()
+ protected function testQueryOrderMethod()
{
- echo "测试查询订单方法...";
+ require_once dirname(__DIR__) . '/data/sdk/utils.php';
+ require_once dirname(__DIR__) . '/data/sdk/HuaweiPayClient.php';
+
+ // 添加必要的配置
+ $site_id = '1';
+ $config_data = $this->config;
+ $config_data['site_id'] = $site_id;
+ // 使用文本模式的密钥,而不是证书文件
+ unset($config_data['private_key']);
+ unset($config_data['huawei_public_key']);
try {
- // 模拟配置
- $this->mockConfig();
+ // 创建实际的华为支付客户端实例
+ $project_root = dirname(__DIR__, 4); // 获取项目根目录
+ $huawei_pay_client = new \addon\huaweipay\data\sdk\HuaweiPayClient($config_data, $project_root);
+ echo " - 华为支付客户端实例创建成功\n";
- // 尝试创建Pay实例
- $payModel = new Pay($this->siteId);
+ // 测试查询订单
+ $params = [
+ 'out_trade_no' => 'TEST_QUERY_' . time()
+ ];
- echo " ⚠️ 跳过实际查询订单测试\n";
- } catch (\Exception $e) {
- echo " ⚠️ 查询订单测试跳过: " . $e->getMessage() . "\n";
+ // 调用真实的查询订单方法
+ $query_result = $huawei_pay_client->queryOrder($params);
+ echo " - 查询订单方法调用成功\n";
+ echo " 结果: " . json_encode($query_result, JSON_UNESCAPED_UNICODE) . "\n";
+
+ } catch (Exception $e) {
+ echo " - 查询订单测试失败: " . $e->getMessage() . "\n";
+ throw $e;
}
}
/**
- * 模拟配置
+ * 测试回调验证方法
*/
- private function mockConfig()
+ protected function testCallbackVerification()
{
- // 这里可以添加更高级的模拟技术,例如使用PHPUnit的mock对象
- // 目前我们只需要确保配置数据能正确加载
- echo "\n使用配置数据:\n";
- echo "App ID: {$this->configData['app_id']}\n";
- echo "商户号: {$this->configData['mch_id']}\n";
- echo "证书ID: {$this->configData['mch_auth_id']}\n";
+ require_once dirname(__DIR__) . '/data/sdk/utils.php';
+ require_once dirname(__DIR__) . '/data/sdk/HuaweiPayClient.php';
+
+ // 添加必要的配置
+ $site_id = '1';
+ $config_data = $this->config;
+ $config_data['site_id'] = $site_id;
+ // 使用文本模式的密钥,而不是证书文件
+ unset($config_data['private_key']);
+ unset($config_data['huawei_public_key']);
+
+ try {
+ // 创建实际的华为支付客户端实例
+ $project_root = dirname(__DIR__, 4); // 获取项目根目录
+ $huawei_pay_client = new \addon\huaweipay\data\sdk\HuaweiPayClient($config_data, $project_root);
+ echo " - 华为支付客户端实例创建成功\n";
+
+ // 生成模拟回调参数并签名
+ $timestamp = date('Y-m-d H:i:s');
+ $callback_params = [
+ 'out_trade_no' => 'TEST_CALLBACK_' . time(),
+ 'trade_no' => 'HWPAY_' . time(),
+ 'total_amount' => '0.01',
+ 'trade_status' => 'SUCCESS',
+ 'timestamp' => $timestamp,
+ 'app_id' => $config_data['app_id'],
+ 'merc_no' => $config_data['merc_no'],
+ 'sign_type' => 'RSA2'
+ ];
+
+ // 生成签名
+ $sign = '';
+ $signParams = $callback_params;
+ unset($signParams['sign']);
+ unset($signParams['sign_type']);
+ ksort($signParams);
+
+ $stringToSign = '';
+ foreach ($signParams as $key => $value) {
+ $stringToSign .= $key . '=' . $value . '&';
+ }
+ $stringToSign = rtrim($stringToSign, '&');
+
+ // 使用文本模式的私钥生成签名
+ $private_key_content = $config_data['private_key_text'];
+
+ // 对私钥进行格式化,确保openssl_sign能够正确处理
+ $cleanContent = preg_replace('/\s+/', '', $private_key_content);
+ $cleanContent = preg_replace('/-----BEGIN.*?-----/', '', $cleanContent);
+ $cleanContent = preg_replace('/-----END.*?-----/', '', $cleanContent);
+ $formattedKey = "-----BEGIN PRIVATE KEY-----\n";
+ $length = strlen($cleanContent);
+ for ($i = 0; $i < $length; $i += 64) {
+ $formattedKey .= substr($cleanContent, $i, 64) . "\n";
+ }
+ $formattedKey .= "-----END PRIVATE KEY-----";
+
+ openssl_sign($stringToSign, $sign, $formattedKey, OPENSSL_ALGO_SHA256);
+ $callback_params['sign'] = base64_encode($sign);
+
+ // 使用华为支付客户端验证签名
+ $verify_result = $huawei_pay_client->verifySign($callback_params);
+
+ if ($verify_result) {
+ echo " - 回调签名验证通过\n";
+ echo " - 回调参数:" . json_encode($callback_params, JSON_UNESCAPED_UNICODE) . "\n";
+ echo " - 交易状态:{$callback_params['trade_status']}\n";
+ } else {
+ throw new Exception("回调签名验证失败");
+ }
+
+ } catch (Exception $e) {
+ echo " - 回调验证测试失败: " . $e->getMessage() . "\n";
+ throw $e;
+ }
}
}
-// 如果直接运行此文件,则执行测试
-if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) {
- $test = new PayModelTest();
- $test->runTests();
-}
\ No newline at end of file
+// 运行测试
+$test = new PayModelTest();
+$test->runTests();
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/README.md b/src/addon/huaweipay/tests/README.md
deleted file mode 100644
index 51faf6898..000000000
--- a/src/addon/huaweipay/tests/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# 测试
-
-由于不引入PHPUnit,所以测试用例只能手动执行。
-
-
-```bash
-cd src/
-php addon/huaweipay/tests/HuaweiPayClientTest.php
-php addon/huaweipay/tests/PayModelTest.php
-```
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/TestCase.php b/src/addon/huaweipay/tests/TestCase.php
new file mode 100644
index 000000000..042c5534a
--- /dev/null
+++ b/src/addon/huaweipay/tests/TestCase.php
@@ -0,0 +1,117 @@
+loadConfigData();
+ }
+
+ /**
+ * 加载配置数据
+ */
+ protected function loadConfigData()
+ {
+ $yamlPath = __DIR__ . '/mock/data.yml';
+ if (file_exists($yamlPath)) {
+ if (function_exists('yaml_parse_file')) {
+ $this->configData = yaml_parse_file($yamlPath);
+ } else {
+ // 如果没有yaml_parse_file函数,手动解析
+ $this->configData = $this->parseYamlFile($yamlPath);
+ }
+ } else {
+ $this->fail("配置文件不存在: {$yamlPath}");
+ }
+ }
+
+ protected function formatCertificateContent($content, $type = 'public_key')
+ {
+ return Utils::formatCertificateContent($content, $type);
+ }
+
+ /**
+ * 手动解析YAML文件
+ * @param string $filePath
+ * @return array
+ */
+ private function parseYamlFile($filePath)
+ {
+ $content = file_get_contents($filePath);
+ $lines = explode("\n", $content);
+ $result = [];
+ $currentKey = '';
+ $isMultiline = false;
+ $multilineValue = '';
+
+ foreach ($lines as $line) {
+ $line = trim($line);
+
+ // 跳过注释和空行
+ if (empty($line) || strpos($line, '#') === 0) {
+ continue;
+ }
+
+ // 处理多行值
+ if ($isMultiline) {
+ if (strpos($line, '-----END') === 0) {
+ $multilineValue .= $line . "\n";
+ $result[$currentKey] = rtrim($multilineValue);
+ $isMultiline = false;
+ $multilineValue = '';
+ } else {
+ $multilineValue .= $line . "\n";
+ }
+ continue;
+ }
+
+ // 处理单行键值对
+ if (strpos($line, ':') !== false) {
+ list($key, $value) = explode(':', $line, 2);
+ $key = trim($key);
+ $value = trim($value);
+
+ // 处理多行值开始
+ if ($value === '|') {
+ $isMultiline = true;
+ $currentKey = $key;
+ continue;
+ }
+
+ // 处理普通值
+ if (!empty($value)) {
+ $result[$key] = $value;
+ }
+ }
+ }
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/mock/cert/huawei_public_key.pem b/src/addon/huaweipay/tests/mock/cert/huawei_public_key.pem
index 76cae1b46..10711ef74 100644
--- a/src/addon/huaweipay/tests/mock/cert/huawei_public_key.pem
+++ b/src/addon/huaweipay/tests/mock/cert/huawei_public_key.pem
@@ -1 +1,3 @@
------BEGIN PUBLIC KEY-----\nMOCK_PUBLIC_KEY_CONTENT\n-----END PUBLIC KEY-----
\ No newline at end of file
+-----BEGIN PUBLIC KEY-----
+MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA9g1+QcqvC4f1pUiwJ1um1iBUlNn6hRDJrNdv5zB77l5DNo6S6hE4w7VyhkMnkIk89i8kTej1m1ByjRpo7B5OPqafNqI9JBQyQ26A1Zp71zSfe/UicAFiMtF4lWNnAHBYH06sUTvybwYllDVybpi6lL2i8VAGIN8YgoK36lPaYsxWZ911lPCegy7B3kDj1xhBe41cNHgu8wYmjqLU7njleY5Pseherx+Kb58aQvB5xQr8w7KgAyMrsfRH30Btpg/ZWRn8qOXd/DW6eEla3djah4ug8jKdi0qUkA24FLDdOZST4vb5qhgQDVXpqJhYmBIU14YOHsCX9Olu6b7DDjQo/dvOaY3vzWROfV+sV60fUVIps8Vy1EpS/UXeHUxg6r37U8WAxUbSV8d6e4VylLuiIgbX5JpSC1s7jq/cwUwXfSJmKzaCj+C+LJ958IM17FYxIz5xWJtZEzWsPAH7WVCP3b1m4MHU/UwGuMu/Gfdzusnr+Qtan6Wqn9AqUyJP/JfrAgMBAAE=
+-----END PUBLIC KEY-----
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/mock/cert/merchant_private_key.pem b/src/addon/huaweipay/tests/mock/cert/merchant_private_key.pem
index c9ed9b515..2514642be 100644
--- a/src/addon/huaweipay/tests/mock/cert/merchant_private_key.pem
+++ b/src/addon/huaweipay/tests/mock/cert/merchant_private_key.pem
@@ -1,8 +1,3 @@
-----BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2m2vKv9jH0QZc7K
-+1q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q
-3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q
-3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q
-3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q
-3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q3q
+MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQD2DX5Byq8Lh/WlSLAnW6bWIFSU2fqFEMms12/nMHvuXkM2jpLqETjDtXKGQyeQiTz2LyRN6PWbUHKNGmjsHk4+pp82oj0kFDJDboDVmnvXNJ979SJwAWIy0XiVY2cAcFgfTqxRO/JvBiWUNXJumLqUvaLxUAYg3xiCgrfqU9pizFZn3XWU8J6DLsHeQOPXGEF7jVw0eC7zBiaOotTueOV5jk+x6F6vH4pvnxpC8HnFCvzDsqADIyux9EffQG2mD9lZGfyo5d38Nbp4SVrd2NqHi6DyMp2LSpSQDbgUsN05lJPi9vmqGBANVemomFiYEhTXhg4ewJf06W7pvsMONCj9285pje/NZE59X6xXrR9RUimzxXLUSlL9Rd4dTGDqvftTxYDFRtJXx3p7hXKUu6IiBtfkmlILWzuOr9zBTBd9ImYrNoKP4L4sn3nwgzXsVjEjPnFYm1kTNaw8AftZUI/dvWbgwdT9TAa4y78Z93O6yev5C1qfpaqf0CpTIk/8l+sCAwEAAQKCAYAVlJFiS9iWdlJBMOLiUNONLEC+3W9vhE1r72lNKZ91BKd4fYC9Ls1/vMZSqEksEB1cqj3Q54HDIYcqgQp6yx2puQt1yzz5kRvndiWulmIOOftS7+kZUcW/F0gwMguyqifQdyH97fgRbMSW/ykOMi8LJKbJ627eKzMHH1fqIXih+bIKYg4SBhihANTYHXDeSK5Vm8xefbwAbKWtFPMAB3J4+tZakDrduTJ3H8k53cWQVqpcr6oBHHCUpww2tHvpeLI3a/FXyHYBqrx8ErnXCjkVBHBtwQf+43H+buyDjrYUwJUi3RSJgeefcyyJoO0I9GwGb6nY8kK5kZ0aeIIkfiVOexMYS9w11FVl96LjC7HjJQrNY4jOLI/X76xyEwBFy9LNogRTafjZVZmVRj/9Kembx6+/eDxvyEv5FnelsHqfFbv1KPkR6e7FvwpDbYgJBpfKTE6SICqo52bHxewpSL7KHRlTpp0IfMM/IOOs8CCwI37ixKQun6W4en457j/tP4ECgcEA+0pa3omCKJfiVR4PpyXY+t9FEqdcZkdYEDtCSX9vqu6yE4sdt7rNnZ6cIiD6ViC2xvQ5VLry1y57JvP57svt5OJYhmILuj3jJ7pdcfKRAVaV4W7C8vGMit6ssckEOnOSj/bvEazfHorjXKU9cn9uCymczRQC9azaRkN+1LLk2mBbczghxd7rSJyXtbt9NVNUi9Gr11AzJsrWN/Bqqna5BizBLIaGYthJ/04LYTl1AjWPEJXjXI72/WvMdQ/w6itxAoHBAPqqAi5mbvjRDitmLQchc/R3Yl329OaB3GSz3YtFqfk9lLJIyl91+MzxBg1ChHnRFC8MB1s1Z05ZkFmcy8AVqvJ6MSZhUCLNjq8HyVgp24CqQTr8ciomF03ncXnKKSsH7tidInkQ8R3e7qRRGQMnDvV+Hs3FjlOk8vJyZCcGkUCVA0kkwkk/95qIfrZsthtewAxJR/rY63FnG+MUayEBB1lwyJZ7xDi/GyX6SAnPBl6bLRA1BdBeTV8K0MEwwIqzGwKBwBqo+9UKT7XQz2FqbAy2tjt/fouJGAN95DjsoI69p3JCGsB6DPAWMIRddIEmcIi8tceL151GrEbqFoS+c7DDD/0timjPdCEROc1YN1vEeV/j+MjPAH3X5KpDD51ZD0rIQi9l6l08svtBjvegTFGedWVXx9v2GI5KBWpY9NbKF/+XI3yo4uRkTyAIBQxx1MnYimq/FvUj/BlMgceziQ2GxQCDtQbtSsqn2cntVMW+28wdNI106YdDX67pRerRgyTE8QKBwHAwLxG9XuWWC5V5AaYzXsaHuEr+ANY6QP4BUqLG5zBaU3cIBSt8jYKMTX0ZzFkJLtNvussjt7zlcSnqd3bdO8mSzvSykT9CaR4FiiQfd9K6YL+ZxS8AJWYEtFEiHhLYVho1Gfy9jG0mHgEFGwDCNnvBmt/WD8F4DhRdBl5BHjmdd/8AqMRIEPXlKXFUbp0JZ0MYeVLYS2hSEbUsqlX3M+bgB6bydfw/7FKvFhbtxZgKM70RPizoSBDFsnEE9OgfCQKBwQD4M4f2f678YScvrwQnV2J8SSnJhQbZqht1Rlb34zU0JIcfpwOKpoNvRyZz1BxvzPSm5S3TLDytK16uvAsMFCHVByyCajPozlWnLzeEqvE4DZXZcWeY+/+MZ4nsajLTTeJWos6UrazZcd8/2c301x4OUKfgZWW1tS65MA48X0+Y4uPyOuH365wQ0obBfg4aB85sZl71v7Rveq+COHBstp/BdDVAsdufWCr7Nkbeu/3qh886ZumpYe+89So1oDIuoys=
-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/src/addon/huaweipay/tests/mock/data.yml b/src/addon/huaweipay/tests/mock/data.yml
index c6867fb71..5f0783243 100644
--- a/src/addon/huaweipay/tests/mock/data.yml
+++ b/src/addon/huaweipay/tests/mock/data.yml
@@ -1,26 +1,34 @@
-# 华为支付测试用简单配置
+# ========华为支付测试用简单配置==========
+
+# 沙盒模式
+sandbox: true
# 商户应用ID,(例如:快应用)
app_id: 115644647
# 商户号
-mch_id: 102751500028
+merc_no: 102751500028
# 商户证书ID
mch_auth_id: 10086000901972225
+# 商户应用私有密钥(支付私钥) - 文件路径
+private_key: mock/cert/merchant_private_key.pem
-# 商户应用私有密钥(支付私钥)
-privateKey: |
+# 商户应用私有密钥(支付私钥) - 文本内容
+private_key_text: |
-----BEGIN PRIVATE KEY-----
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
-----END PRIVATE KEY-----
-# 华为支付公钥
-huawei_public_key: |
+# 华为支付公钥 - 文件路径
+huawei_public_key: mock/cert/huawei_public_key.pem
+
+# 华为支付公钥 - 文本内容
+huawei_public_key_text: |
-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA9g1+QcqvC4f1pUiwJ1um1iBUlNn6hRDJrNdv5zB77l5DNo6S6hE4w7VyhkMnkIk89i8kTej1m1ByjRpo7B5OPqafNqI9JBQyQ26A1Zp71zSfe/UicAFiMtF4lWNnAHBYH06sUTvybwYllDVybpi6lL2i8VAGIN8YgoK36lPaYsxWZ911lPCegy7B3kDj1xhBe41cNHgu8wYmjqLU7njleY5Pseherx+Kb58aQvB5xQr8w7KgAyMrsfRH30Btpg/ZWRn8qOXd/DW6eEla3djah4ug8jKdi0qUkA24FLDdOZST4vb5qhgQDVXpqJhYmBIU14YOHsCX9Olu6b7DDjQo/dvOaY3vzWROfV+sV60fUVIps8Vy1EpS/UXeHUxg6r37U8WAxUbSV8d6e4VylLuiIgbX5JpSC1s7jq/cwUwXfSJmKzaCj+C+LJ958IM17FYxIz5xWJtZEzWsPAH7WVCP3b1m4MHU/UwGuMu/Gfdzusnr+Qtan6Wqn9AqUyJP/JfrAgMBAAE=
-----END PUBLIC KEY-----
-notifyUrl: https://test.example.com/huawei-pay/notify
-returnUrl: https://test.example.com/huawei-pay/return
+notifyUrl: https://dev.aigc-quickapp.com/pay/pay/notify.html
+returnUrl: https://dev.aigc-quickapp.com/pay/pay/payreturn.html
diff --git a/src/addon/huaweipay/tests/phpunit.xml.dist b/src/addon/huaweipay/tests/phpunit.xml.dist
new file mode 100644
index 000000000..9837797bd
--- /dev/null
+++ b/src/addon/huaweipay/tests/phpunit.xml.dist
@@ -0,0 +1,26 @@
+
+
+
+
+ ./
+
+
+
+
+ ../
+
+ ../vendor/
+ ../tests/
+
+
+
+
+
+
+
+
\ No newline at end of file