PAYATHON 2026

支付宝支付时存入body的信息能否用于对账单筛选

支付阿杰

结论

不能直接用支付请求中的 body 查询或筛选支付宝对账单。

alipay.data.dataservice.bill.downloadurl.query 接口用于根据账单类型和账单日期获取账单文件的下载地址。可用的筛选条件以支付宝官方文档当前列出的请求参数为准,通常包括:

  • bill_type:账单类型
  • bill_date:账单日期

该接口没有 body 筛选参数,也不能按商品描述、商户订单号等交易字段在服务端过滤账单。

如果要根据 body 中的信息核对交易,需要先下载完整账单,再由商户系统进行关联或筛选。实际应用中,更适合使用 out_trade_no 作为关联键,不要依赖 body。

为什么不能直接筛选

支付接口中的 body 一般用于描述商品或订单内容,属于交易请求信息。对账单下载接口则按日期生成账单文件,两者用途不同。

调用账单下载地址查询接口时,支付宝返回的是已生成账单文件的下载地址,并不会重新查询交易明细。因此,即使支付时传入了 body,也不能按下面的方式筛选账单:

{
  "bill_type": "trade",
  "bill_date": "2026-09-07",
  "body": "某个业务标识"
}

body 不是该接口规定的查询条件,额外传入未定义字段也不能作为有效的筛选方式。

至于 body 是否会出现在下载后的账单文件中,以及它对应哪个账单列,需要查看实际账单类型、文件表头和支付宝当前文档。支付请求中传入了 body,并不代表账单一定会完整保留这个字段。

推荐处理方式

1. 支付时保存业务关联关系

创建支付订单时,把业务信息保存在商户自己的数据库中,并通过唯一的 out_trade_no 建立关联。

例如:

out_trade_no: ORDER_20260908_0001
body: 会员充值-季度套餐
business_type: MEMBER_RECHARGE
business_id: 983721

建议至少保存这些信息:

out_trade_no
支付宝交易号 trade_no
body
业务类型
业务记录 ID
订单金额
订单状态
创建时间
支付时间

out_trade_no 应保持唯一和稳定,用来关联商户订单与支付宝账单记录。

2. 获取账单下载地址

下面的示例使用支付宝 Java SDK。SDK 类名和配置方式可能因版本而异,请以项目实际依赖的版本为准。

AlipayClient alipayClient = new DefaultAlipayClient(
    gatewayUrl,
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayDataDataserviceBillDownloadurlQueryRequest request =
    new AlipayDataDataserviceBillDownloadurlQueryRequest();

request.setBizContent("""
{
  "bill_type": "trade",
  "bill_date": "2026-09-07"
}
""");

AlipayDataDataserviceBillDownloadurlQueryResponse response =
    alipayClient.execute(request);

if (response.isSuccess()) {
    String downloadUrl = response.getBillDownloadUrl();
    System.out.println(downloadUrl);
} else {
    System.err.println(response.getCode());
    System.err.println(response.getSubCode());
    System.err.println(response.getSubMsg());
}

这里仅通过 bill_type 和 bill_date 获取账单下载地址,不能附加 body 条件。

3. 下载并解析账单

取得 bill_download_url 后应及时下载文件。下载地址通常有时效性,不适合保存很久后再使用。

解析账单前,先检查文件的实际表头,确认商户订单号对应的列,再与本地订单表关联。不要依赖固定的列序号,因为账单格式可能调整,不同账单类型包含的字段也可能不同。

伪代码如下:

for (BillRecord record : billRecords) {
    String outTradeNo = record.getOutTradeNo();

    MerchantOrder order = orderRepository.findByOutTradeNo(outTradeNo);
    if (order == null) {
        // 记录支付宝账单中存在、商户系统中不存在的订单
        continue;
    }

    if ("MEMBER_RECHARGE".equals(order.getBusinessType())) {
        // 对该业务类型执行金额、状态等对账检查
        reconcile(record, order);
    }
}

如果只是临时分析,并且已经确认下载的账单中包含可以对应 body 的列,也可以在本地做文本筛选。例如,用 Python 读取 CSV:

import csv

keyword = "会员充值"

with open("alipay_bill.csv", "r", encoding="gb18030", newline="") as file:
    reader = csv.DictReader(file)

    for row in reader:
        product_name = row.get("商品名称", "")
        if keyword in product_name:
            print(row)

账单文件的编码、分隔符、列名和文件结构都要以实际下载内容为准。示例中的 "商品名称" 只是为了说明本地筛选方法,不能认为所有支付宝账单都有这个固定字段。

更可靠的设计

不要把需要精确筛选的业务标识只放在 body 中,主要有以下原因:

  • body 是描述性文本,不一定唯一。
  • 文本可能因业务展示需要而改变。
  • 账单下载接口不能按该字段过滤。
  • 下载的账单中不一定有与支付请求 body 完全一致的字段。
  • 模糊匹配可能造成漏单或误匹配。

可以采用下面的数据关系:

支付宝账单
    out_trade_no
          │
          ▼
商户订单表
    out_trade_no
    business_type
    business_id
    body

筛选某类业务时,先从商户订单表中查出符合条件的 out_trade_no,再与支付宝账单匹配:

SELECT
    b.*,
    o.business_type,
    o.business_id,
    o.body
FROM alipay_bill b
JOIN merchant_order o
    ON b.out_trade_no = o.out_trade_no
WHERE o.business_type = 'MEMBER_RECHARGE';

正式对账时,这种方式比在账单文本中搜索 body 更可靠,也更方便处理退款、金额差异、状态不一致和重复记录。

注意事项

  • 账单下载地址查询接口返回的是下载地址,不是经过条件过滤的交易列表。
  • 不要用支付请求中的 body 充当唯一订单标识。
  • 对账时优先使用 out_trade_no,必要时结合支付宝交易号 trade_no。
  • 不要把未经编码的复杂业务数据直接放入 body,该字段主要用于交易描述。
  • 保存账单原文件,同时记录下载日期、账单类型和解析结果,以便后续追溯。
  • 解析前先识别账单中的说明行、汇总行和明细表头,避免把非交易行当成交易记录。
  • 比较金额时应使用 BigDecimal 等十进制定点类型,不要使用 double。
  • 如果业务需要实时查询单笔交易,而不是按日对账,应调用支付宝提供的交易查询接口,并使用 out_trade_no 或 trade_no 查询,不要调用账单下载接口。

备注:内容仅供参考。