获取对账单成功但访问 bill_download_url 提示 error
支付老李
结论
接口已经调用成功。浏览器提示 error,通常是因为下载地址输出到页面时被 HTML 解析,URL 中的:
×tamp=
被错误显示为:
×tamp=
浏览器把其中的 × 识别成了 HTML 实体 ×,导致下载参数损坏。示例响应里已经能看到这个问题:
...20210409.csv.zip×tamp=1618215335&token=...
正确的地址应该是:
...20210409.csv.zip×tamp=1618215335&token=...
不要从浏览器渲染后的 print_r() 结果中复制链接。应直接读取接口返回的原始 bill_download_url 并下载文件。
原因说明
bill_download_url 是一个带查询参数的临时下载地址,各参数之间使用 & 分隔。URL 直接输出到 HTML 页面后,浏览器可能会把 ×tamp 开头的 × 当作 HTML 实体,于是:
×tamp=1618215335
会显示成:
×tamp=1618215335
支付宝账单下载服务收到的参数不完整,便可能返回 error。
账单下载地址通常还有时效限制。即使 URL 格式正确,获取后长时间没有下载,也可能因为地址过期而失败。具体有效期应以当前支付宝接口文档和实际响应为准。链接过期后,需要重新调用接口获取。
正确下载方式
1. 不要复制页面上显示的 URL
直接从 SDK 返回对象中读取字段:
$downloadUrl =
$response
->alipay_data_dataservice_bill_downloadurl_query_response
->bill_download_url;
读取后立即通过程序下载,不要先用 print_r() 输出,再从浏览器页面复制。
2. 使用 cURL 下载账单
<?php
$result = $response->alipay_data_dataservice_bill_downloadurl_query_response;
if ((string) $result->code !== '10000') {
throw new RuntimeException(
sprintf(
'获取账单下载地址失败:code=%s, msg=%s',
$result->code ?? '',
$result->msg ?? ''
)
);
}
$downloadUrl = (string) $result->bill_download_url;
$savePath = __DIR__ . '/alipay_bill.csv.zip';
$fileHandle = fopen($savePath, 'wb');
if ($fileHandle === false) {
throw new RuntimeException('无法创建账单文件');
}
$curl = curl_init($downloadUrl);
curl_setopt_array($curl, [
CURLOPT_FILE => $fileHandle,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 120,
CURLOPT_FAILONERROR => false,
]);
$success = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$error = curl_error($curl);
curl_close($curl);
fclose($fileHandle);
if ($success === false || $httpCode < 200 || $httpCode >= 300) {
@unlink($savePath);
throw new RuntimeException(
sprintf(
'下载账单失败:HTTP %d,cURL error=%s',
$httpCode,
$error
)
);
}
echo '账单已保存至:' . $savePath;
保存文件时要使用 wb,以二进制方式写入,否则 ZIP 文件在部分运行环境中可能损坏。
3. 如果必须在页面中展示链接
输出到 HTML 时需要转义,避免浏览器把查询参数解析成实体:
echo '<a href="'
. htmlspecialchars($downloadUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '">下载支付宝对账单</a>';
如果只是排查原始值,可以改为输出纯文本:
header('Content-Type: text/plain; charset=UTF-8');
var_dump($downloadUrl);
也可以查看页面源代码,不要只看浏览器渲染后的文字。
临时修正示例
如果已经确认问题只是复制 URL 时将 ×tamp 变成了 ×tamp,可以临时修正:
$downloadUrl = str_replace('×tamp=', '×tamp=', $downloadUrl);
这种方式只适合排查问题,不能代替对接口响应的正确处理。更可靠的方式仍是直接读取 SDK 返回的原始字段,因为 URL 中可能还有其他必须原样保留的编码内容。
注意事项
code = 10000只表示成功获取了下载地址,不代表经过任意处理或复制后的 URL 都能正常下载。- 不要对整个下载地址直接执行
urlencode(),否则://、?、&、=等结构也会被编码,地址将无法使用。 - 不要手动拼接或解码
token、fileId等参数,应完整使用接口返回值。 - 获取 URL 后应尽快下载。如果提示地址失效,请重新调用
alipay.data.dataservice.bill.downloadurl.query获取新地址。 - 如果程序中的原始字符串已经是
×tamp=,需要检查中间环节是否经过富文本编辑器、HTML 解析、实体解码或人工复制。 - 下载失败时,应同时记录 HTTP 状态码和响应正文。如果 URL 参数正确、地址刚刚生成却仍然失败,再检查账单日期、账单类型、应用权限,以及支付宝账号中是否存在对应账单。
备注:内容仅供参考。
