diff --git a/WePayV3/Contracts/BasicWePay.php b/WePayV3/Contracts/BasicWePay.php
index c271c4c..c98f068 100644
--- a/WePayV3/Contracts/BasicWePay.php
+++ b/WePayV3/Contracts/BasicWePay.php
@@ -206,6 +206,83 @@ abstract class BasicWePay
         return $isjson ? json_decode($content, true) : $content;
     }
 
+    /**
+     * 模拟发起上传请求
+     * @param string $pathinfo 请求路由
+     * @param string $filename 文件本地路径
+     * @param boolean $verify 是否验证
+     * @param boolean $isjson 返回JSON
+     * @return array|string
+     * @throws \WeChat\Exceptions\InvalidResponseException
+     */
+    public function doUpload($pathinfo, $filename, $verify = false, $isjson = true)
+    {
+        $filedata = file_get_contents($filename);
+        $fileinfo = [
+            'sha256'   => hash("sha256", $filedata),
+            'filename' => basename($filename)
+        ];
+        $jsondata = json_encode($fileinfo);
+        list($time, $nonce) = [time(), uniqid() . rand(1000, 9999)];
+        $signstr = join("\n", ['POST', $pathinfo, $time, $nonce, $jsondata, '']);
+        // 生成签名
+        $sign = $this->signBuild($signstr);
+        // 生成数据签名TOKEN
+        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
+            $this->config['mch_id'], $nonce, $time, $this->config['cert_serial'], $sign
+        );
+        $location = (preg_match('|^https?://|', $pathinfo) ? '' : $this->base) . $pathinfo;
+        $boundary = mt_rand(100000000000000000, 999999999999999999);
+        $header = [
+            'Accept: application/json',
+            "Content-Type: multipart/form-data; boundary={$boundary}",
+            'User-Agent: https://thinkadmin.top',
+            "Authorization: WECHATPAY2-SHA256-RSA2048 {$token}",
+            "serial_no: {$this->config['mp_cert_serial']}",
+            "nonce_str: {$nonce}",
+            "signature: {$sign}"
+        ];
+        $lines = [];
+        $line[] = "--{$boundary}";
+        $line[] = "Content-Disposition: form-data; name=\"meta\"";
+        $line[] = "Content-Type: application/json";
+        $line[] = "";
+        $line[] = $jsondata;
+        $line[] = "--{$boundary}";
+        $line[] = "Content-Disposition: form-data; name=\"file\"; filename=\"{$fileinfo['filename']}\";";
+        $line[] = "Content-Type: image/jpg";
+        $line[] = "";
+        $line[] = $filedata;
+        $line[] = "--{$boundary}--";
+        $postdata = join("\r\n", $line);
+        list($header, $content) = $this->_doRequestCurl('POST', $location, [
+            'data' => $postdata, 'header' => $header,
+        ]);
+        if ($verify) {
+            $headers = [];
+            foreach (explode("\n", $header) as $line) {
+                if (stripos($line, 'Wechatpay') !== false) {
+                    list($name, $value) = explode(':', $line);
+                    list(, $keys) = explode('wechatpay-', strtolower($name));
+                    $headers[$keys] = trim($value);
+                }
+            }
+            try {
+                if (empty($headers)) {
+                    return $isjson ? json_decode($content, true) : $content;
+                }
+                $string = join("\n", [$headers['timestamp'], $headers['nonce'], $content, '']);
+                if (!$this->signVerify($string, $headers['signature'], $headers['serial'])) {
+                    throw new InvalidResponseException('验证响应签名失败');
+                }
+            } catch (\Exception $exception) {
+                throw new InvalidResponseException($exception->getMessage(), $exception->getCode());
+            }
+        }
+        return $isjson ? json_decode($content, true) : $content;
+    }
+
+
     /**
      * 通过CURL模拟网络请求
      * @param string $method 请求方法