修正上传的逻辑

develop
JING 2 months ago
parent 25687e8b2c
commit 348de5cb19
  1. 33
      src/factory/object/onelevel/UploadRequest.php
  2. 114
      src/http/impl/HFPayHttpClient.php

@ -66,37 +66,6 @@ class UploadRequest
if(empty($this->data['handle_type'])) {
throw new ParamsException('上传类型不能为空', 'handle_type');
}
// 构建 multipart 数据结构
$multipartData = [
[
'name' => 'attach_file', // 这里的 'file' 是接口定义的文件字段名
'contents' => fopen($this->data['attach_file'], 'r'), // 使用 fopen 打开文件流
],
// 可以同时上传其他文本字段
[
'name' => 'attach_no',
'contents' => $this->data['attach_no'],
],
[
'name' => 'trans_type',
'contents' => $this->data['trans_type'],
],
[
'name' => 'attach_type',
'contents' => $this->data['attach_type'],
],
[
'name' => 'attach_desc',
'contents' => $this->data['attach_desc'],
],
[
'name' => 'handle_type',
'contents' => $this->data['handle_type'],
],
];
return $multipartData;
return $this->data;
}
}

@ -114,7 +114,61 @@ class HFPayHttpClient implements IHttpClient
*/
private ISigner $signClient;
/**
* 准备 Multipart 数据
* 核心原则:文件流只进 multipart,不进 signData;文本字段两者都进
*/
private function _prepareMultipartData(array $body): array
{
$multipart = [];
$signData = [];
foreach ($body as $key => $value) {
// 1. 资源类型(文件流):仅放入 multipart,绝不参与签名
if (is_resource($value)) {
$multipart[] = [
'name' => $key,
'contents' => $value,
];
continue;
}
// 2. 已是 Guzzle Multipart 格式的数组
if (is_array($value) && isset($value['contents'])) {
$multipart[] = $value;
// 仅当 contents 非资源时才参与签名
if (!is_resource($value['contents'])) {
$signData[$key] = (string)$value['contents'];
}
continue;
}
// 3. 普通数组:转 JSON 字符串
if (is_array($value)) {
$jsonStr = json_encode($value, JSON_UNESCAPED_UNICODE);
$multipart[] = ['name' => $key, 'contents' => $jsonStr];
$signData[$key] = $jsonStr;
continue;
}
// 4. NULL 值:转为空字符串(汇付通常要求空值也参与签名)
if ($value === null) {
$multipart[] = ['name' => $key, 'contents' => ''];
$signData[$key] = '';
continue;
}
// 5. 标量类型:强制转为字符串
$strVal = (string)$value;
$multipart[] = ['name' => $key, 'contents' => $strVal];
$signData[$key] = $strVal;
}
return [
'multipart' => $multipart,
'sign_data' => $signData,
];
}
/**
* @param string $method
* @param string $uri
@ -131,31 +185,41 @@ class HFPayHttpClient implements IHttpClient
{
$options = $this->getRequestOptions();
if(!empty($query)) {
if (!empty($query)) {
$options['query'] = array_filter($query, fn($v) => $v !== null);
}
//添加固定的参数
//版本号
if(empty($body['version'])) {
$body['version'] = $this->config['version'] ?? '';
// ✅ 关键修正:固定参数必须在签名之前注入到 $body 中
// 这样无论是 form/json 还是 multipart,都能保证这些字段参与签名
if (empty($body['version'])) {
$body['version'] = $this->config['version'] ?? self::API_VERSION;
}
if(empty($body['mer_cust_id'])) {
if (empty($body['mer_cust_id'])) {
$body['mer_cust_id'] = $this->config['mid'] ?? '';
}
if(empty($body['bg_ret_url']) && !empty($this->config['callback'])) {
if (empty($body['bg_ret_url']) && !empty($this->config['callback'])) {
$body['bg_ret_url'] = $this->config['callback'];
}
//将内容生成签名
if ($format === 'multipart') {
// 1. 分离文件流和文本参数
$processed = $this->_prepareMultipartData($body);
// 2. 使用纯文本参数生成签名(文件流已被排除)
$sign = $this->signClient->sign($processed['sign_data']);
// 3. 将签名结果作为普通文本字段追加到 multipart
$processed['multipart'][] = [
'name' => 'check_value',
'contents' => $sign,
];
$options['multipart'] = $processed['multipart'];
} else {
// 非 multipart 逻辑保持不变
$sign = $this->signClient->sign($body);
$body['check_value'] = $sign;
// echo '请求参数 #####';
// print_r($body);
// echo '请求参数 end';
if(empty($format)) {
$format = 'form';
}
switch ($format) {
case 'json':
$options['json'] = array_filter($body, fn($v) => $v !== null);
@ -164,31 +228,27 @@ class HFPayHttpClient implements IHttpClient
$options['form_params'] = array_filter($body, fn($v) => $v !== null);
break;
case 'body':
$options['body'] = array_filter($body, fn($v) => $v !== null);
break;
// 新增的分支
case 'multipart':
$options['multipart'] = array_filter($body, fn($v) => $v !== null);
$options['body'] = json_encode(array_filter($body, fn($v) => $v !== null), JSON_UNESCAPED_UNICODE);
break;
}
}
try {
$resp = $this->client->request($method, $uri, $options);
} catch (GuzzleException $e) {
throw new ApiException($e->getMessage());
}
$response = Response::make($resp);
if(!$response->isSuccess()) {
$response = Response::make($resp);
if (!$response->isSuccess()) {
throw new ApiException('汇付反馈状态不正确', $resp->getStatusCode());
}
//验证数据
$body = $response->getBody();
if(!$this->signClient->verify($body)) {
$respBody = $response->getBody();
if (!$this->signClient->verify($respBody)) {
throw new ApiException('签名验证未通过');
}
//解码
$decodeBody = $this->signClient->decode($body['check_value']);
$decodeBody = $this->signClient->decode($respBody['check_value']);
ErrCodeHelper::parseError($decodeBody['resp_code'], $decodeBody['resp_desc']);
return Response::tempInit(200, $decodeBody);
}

Loading…
Cancel
Save