支付宝开放平台调用手册

沙箱查询订单并验证响应

读取已有沙箱订单状态,完整展示 RSA2 参数签名、表单请求和支付宝公钥验签。

官方资料核对 2026-10-15示例代码,未进行真实接口实测
← 返回平台功能

接入准备

  • 已在支付宝沙箱控制台创建或取得沙箱应用,确认该收单辅助接口在当前沙箱支持范围内。
  • 沙箱应用采用普通公钥模式,已配置应用公钥,持有对应应用私钥及可信取得的沙箱支付宝公钥 PEM;证书模式不能直接使用本例。
  • 安装 Node.js 22;准备自己的已有沙箱订单。没有订单时查询会返回交易不存在,不能当作代码签名失败。

准备环境变量

示例值需要替换为自己的凭证或资源 ID。代码在本机或服务端运行;密钥不应放进公开网页。

ALIPAY_APP_ID
沙箱控制台中自己的 AppID,非正式应用 ID。
ALIPAY_PRIVATE_KEY_PATH
自己生成并在沙箱配置对应公钥的应用私钥 PEM 文件,支持 Node 可读的 PKCS#1/PKCS#8。
ALIPAY_PUBLIC_KEY_PATH
同一沙箱可信后台取得的支付宝公钥 PEM 文件;不是应用公钥。
ALIPAY_OUT_TRADE_NO
自己先前已创建的沙箱商户订单号;本页不创建订单。
终端
export ALIPAY_APP_ID='2016091500000000'
export ALIPAY_PRIVATE_KEY_PATH='/secure/sandbox-app-private.pem'
export ALIPAY_PUBLIC_KEY_PATH='/secure/sandbox-alipay-public.pem'
export ALIPAY_OUT_TRADE_NO='SANDBOX_ORDER_20261015'

以上 export 适用于 macOS / Linux 的终端。按调用步骤保存代码文件,再在同一终端执行。

调用步骤

  1. 01

    在同一终端设置下方沙箱环境变量,三项应用与密钥材料必须来自同一套沙箱配置。

  2. 02

    把完整 JavaScript 保存为 request.mjs,用 node request.mjs 运行。代码网关固定沙箱,只发起一个只读查询。

  3. 03

    先验签,再检查业务 code 和订单号;核对 trade_status;WAIT_BUYER_PAY 是待支付,不是收款成功。

调用 01

查询已有沙箱交易

这是 v2 gateway.do 表单接口,method 和 biz_content 是表单字段。下方请求体是字段结构示意;完整代码负责序列化 biz_content、生成当次签名并验证原始响应。

POSThttps://openapi-sandbox.dl.alipaydev.com/gateway.do

请求头

Content-Type
application/x-www-form-urlencoded;charset=utf-8

参数说明

参数表可左右滑动,查看完整说明。

字段 / 位置类型 / 必填说明
app_id请求体string必填沙箱应用 AppID。
method请求体string必填alipay.trade.query,固定接口名称。
charset / format / version请求体string必填本例 utf-8、JSON、1.0。
timestamp请求体string必填北京时间 yyyy-MM-dd HH:mm:ss。
sign_type / sign请求体string必填RSA2;除 sign 和空值外参数按 ASCII 键名排序后,用应用私钥 RSA-SHA256 加签。
biz_content.out_trade_no请求体string必填本例按商户订单号查询,最长64字符;原 API 也允许 trade_no 二选一,同传时 trade_no 优先。

调用代码

替换示例 ID 与请求体内容后运行。

Node.js
import { readFileSync } from 'node:fs';
import { constants, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';
const required = (name) => {
  const value = process.env[name];
  if (!value) throw new Error(`缺少环境变量 ${name}`);
  return value;
};
const appId = required('ALIPAY_APP_ID');
if (!/^\d{16}$/.test(appId)) throw new Error('请填写自己的沙箱 AppID');
const privateKey = createPrivateKey(readFileSync(required('ALIPAY_PRIVATE_KEY_PATH')));
const alipayKey = createPublicKey(readFileSync(required('ALIPAY_PUBLIC_KEY_PATH')));
if (privateKey.asymmetricKeyType !== 'rsa' || privateKey.asymmetricKeyDetails.modulusLength < 2048) {
  throw new Error('本例需要 RSA 2048 位或更长的应用私钥');
}
// 网关固定为官方当前沙箱;本例不允许配置正式网关。
const gateway = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do';
const outTradeNo = required('ALIPAY_OUT_TRADE_NO');
if (!outTradeNo || outTradeNo.length > 64) throw new Error('商户订单号最长 64 字符');
const bizContent = { out_trade_no: outTradeNo };
// 生成北京时间 yyyy-MM-dd HH:mm:ss;机器时钟应保持同步。
const parts = Object.fromEntries(new Intl.DateTimeFormat('en-CA', {
  timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit',
  hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23',
}).formatToParts(new Date()).map((part) => [part.type, part.value]));
const timestamp = `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`;
const parameters = { app_id: appId, method: 'alipay.trade.query', format: 'JSON',
  charset: 'utf-8', sign_type: 'RSA2', timestamp, version: '1.0',
  biz_content: JSON.stringify(bizContent) };
// 先按键名 ASCII 顺序拼接原始值;签名后再做表单 URL 编码。
const signingText = Object.keys(parameters).filter((key) => parameters[key] !== '')
  .sort().map((key) => `${key}=${parameters[key]}`).join('&');
parameters.sign = sign('RSA-SHA256', Buffer.from(signingText, 'utf8'), {
  key: privateKey, padding: constants.RSA_PKCS1_PADDING,
}).toString('base64');
const response = await fetch(gateway, { method: 'POST', redirect: 'error',
  signal: AbortSignal.timeout(20000),
  headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' },
  body: new URLSearchParams(parameters).toString() });
const raw = await response.text();
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const envelope = JSON.parse(raw);
const responseKey = 'alipay_trade_query_response';
if (!envelope[responseKey] || typeof envelope.sign !== 'string') {
  throw new Error('响应缺少预期业务节点或签名,不能作为可信业务结果');
}
// 只提取响应节点的原始 JSON 字符串,不对业务对象重新序列化。
// 扫描时处理 JSON 字符串内的引号与转义,避免正文中的大括号截断节点。
const marker = new RegExp('"' + responseKey + '"\\s*:\\s*').exec(raw);
if (!marker) throw new Error('找不到原始响应节点');
const start = marker.index + marker[0].length;
if (raw[start] !== '{') throw new Error('业务响应节点不是对象');
let depth = 0, inString = false, escaped = false, end = -1;
for (let i = start; i < raw.length; i++) {
  const char = raw[i];
  if (inString) {
    if (escaped) escaped = false;
    else if (char === '\\') escaped = true;
    else if (char === '"') inString = false;
    continue;
  }
  if (char === '"') inString = true;
  else if (char === '{' || char === '[') depth++;
  else if (char === '}' || char === ']') {
    depth--;
    if (depth === 0) { end = i + 1; break; }
  }
}
if (end < 0) throw new Error('业务响应 JSON 不完整');
const signedText = raw.slice(start, end);
if (!verify('RSA-SHA256', Buffer.from(signedText, 'utf8'), {
  key: alipayKey, padding: constants.RSA_PKCS1_PADDING,
}, Buffer.from(envelope.sign, 'base64'))) {
  throw new Error('支付宝响应验签失败,不更新业务记录');
}
// 业务数据仅来自已验签的原始节点,避免重排、转义或未验证字段影响结果。
const result = JSON.parse(signedText);
if (result.code !== '10000') {
  throw new Error(`${result.code}: ${result.sub_code || ''} ${result.sub_msg || result.msg || ''}`);
}
if (result.out_trade_no !== outTradeNo) throw new Error('返回订单号与查询订单不匹配');
console.log(JSON.stringify({ out_trade_no: result.out_trade_no, trade_no: result.trade_no,
  trade_status: result.trade_status, total_amount: result.total_amount }, null, 2));
// 正式业务还必须核对商户、应用、金额和币种,幂等处理;本例只输出沙箱查询结果。

返回示例

已签名响应的结构节选示意;sign 为外层同步响应签名。金额单位为元。本次查询成功与业务成功分开判断。成功付款状态为 TRADE_SUCCESS 或 TRADE_FINISHED,不能将 WAIT_BUYER_PAY 当成功。 以下为结构示意,非本站实测结果。

JSON
{
  "alipay_trade_query_response": {
    "code": "10000",
    "msg": "Success",
    "out_trade_no": "SANDBOX_ORDER_20261015",
    "trade_no": "2026101522000000000000000000",
    "trade_status": "WAIT_BUYER_PAY",
    "total_amount": "1.00"
  },
  "sign": "BASE64_RESPONSE_SIGNATURE_EXAMPLE"
}

常见错误与排查

isv.invalid-signature请求签名无效
核对同套沙箱应用密钥,sign_type 参与签名,先拼原始参数值再 URL 编码;不要拿应用公钥验支付宝响应。
ACQ.TRADE_NOT_EXIST查询交易不存在
确认订单号来自相同沙箱应用和商户;本代码不会创建测试订单。
ACQ.INVALID_PARAMETER业务参数无效
按官方字段核对订单号,不要同时提供互不匹配的两种订单号。

调用注意

  • 请求签名使用应用私钥,响应验签使用可信的支付宝公钥。代码验证原始响应业务节点,不对 JSON 对象重新序列化后验签。
  • 示例只有 Node.js 22,使用内置 crypto 和 fetch,不提供缺少签名的 cURL。
  • 沙箱与正式环境不能混用。本页不会执行下单、退款、转账或关单,也没有在线执行请求功能。

注意事项

  • 仅适用于 OpenAPI v2 的 RSA2 普通公钥模式;不适用于 OpenAPI v3 或证书模式。
  • 沙箱使用模拟数据且无真实交易费用,不代表正式商户资格或资源开销已免除。
  • 未调用真实账号或沙箱 API,未声明签名被平台接受或资金状态已验证。

核验来源

本站按官方资料整理本场景的必要调用步骤与代码,覆盖范围以本页列出的接口为准。官方资料用于追溯规范及查看变更。

  1. 01
  2. 02
  3. 03
    统一收单交易查询 API核验日期 2026-10-15
  4. 04
    RSA2 参数签名规则核验日期 2026-10-15
  5. 05
    公钥模式签名与验签配置核验日期 2026-10-15
  6. 06
  7. 07

支付宝开放平台的其他调用手册

← 返回支付宝开放平台的全部功能