如何调试app支付接口并通过Json拉起支付宝支付页面?
结论
支付接口返回的 JSON 一般只是业务响应,不能直接传给支付宝 SDK。App 需要先解析 JSON,取出服务端生成并签名的 orderString。这个字段也可能叫 orderInfo 或 payInfo。取出后,将字符串原样传给支付宝 App 支付 SDK。
完整流程如下:
App 创建订单
→ 业务服务端调用支付宝 App 支付接口并签名
→ 服务端返回 orderString
→ App 调用支付宝 SDK 拉起支付页面
→ SDK 返回同步结果
→ 支付宝向服务端发送异步通知
→ 服务端验签并更新订单
→ App 查询服务端确认最终状态
控制台可以用来检查接口参数、签名和响应,但不能模拟 App 拉起支付宝。要快速跑通流程,仍然需要一个最小化服务端接口,以及一个已经接入支付宝 SDK 的测试 App。
JSON 应该如何使用
假设服务端返回:
{
"code": 0,
"message": "success",
"data": {
"orderNo": "202609130001",
"orderString": "alipay_sdk=...&app_id=...&biz_content=...&charset=utf-8&method=alipay.trade.app.pay&sign=...&sign_type=RSA2×tamp=..."
}
}
App 应按以下方式处理:
- 判断业务接口是否调用成功。
- 读取
data.orderString。 - 不拆分参数,不调整顺序,也不重新签名。
- 将
orderString原样传给支付宝 SDK。
不要把整个 JSON 交给支付宝 SDK:
错误:SDK.pay(JSON.stringify(response))
正确:SDK.pay(response.data.orderString)
如果接口只返回 biz_content、订单号或一组尚未签名的参数,App 仍然不能直接拉起支付。服务端需要按照支付宝接口要求组装参数并完成 RSA2 签名,生成最终的订单字符串。
服务端生成支付参数
下面的示例只保留了主要结构。不同版本的服务端 SDK 可能使用不同的网关地址、初始化方式和方法签名,请以项目实际采用的支付宝开放平台 SDK 文档为准。
Java 示例
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("202609130001");
model.setSubject("测试商品");
model.setTotalAmount("0.01");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl("https://example.com/api/alipay/notify");
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"创建支付参数失败:" + response.getSubCode() + " " + response.getSubMsg()
);
}
// body 即应返回给 App 的订单字符串
String orderString = response.getBody();
业务接口再将结果包装成 JSON:
Map<String, Object> data = new HashMap<>();
data.put("orderNo", "202609130001");
data.put("orderString", orderString);
return ApiResponse.success(data);
商户私钥只能保存在服务端,不能写入 Android 或 iOS 客户端。否则攻击者可能提取私钥,伪造支付请求。
Android 拉起支付宝
支付宝 Android SDK 常见的调用方式如下:
String orderString = response.getData().getOrderString();
Runnable payRunnable = () -> {
PayTask alipay = new PayTask(this);
Map<String, String> result = alipay.payV2(orderString, true);
runOnUiThread(() -> {
String resultStatus = result.get("resultStatus");
String memo = result.get("memo");
// 此处只适合更新界面状态
// 最终支付结果应向业务服务端查询确认
handlePayResult(resultStatus, memo);
});
};
Thread payThread = new Thread(payRunnable);
payThread.start();
Kotlin 写法如下:
val orderString = response.data.orderString
Thread {
val payTask = PayTask(this)
val result = payTask.payV2(orderString, true)
runOnUiThread {
val resultStatus = result["resultStatus"]
val memo = result["memo"]
// 不要仅凭客户端结果发货
queryOrderStatusFromServer()
}
}.start()
不要在主线程中执行 PayTask.payV2()。线程限制和 SDK 接入方式应以当前集成版本的说明为准。
iOS 拉起支付宝
iOS 端需要配置 URL Scheme,以便支付完成后从支付宝返回 App。示例:
NSString *orderString = response[@"data"][@"orderString"];
[[AlipaySDK defaultService] payOrder:orderString
fromScheme:@"your_app_scheme"
callback:^(NSDictionary *resultDic) {
NSLog(@"支付宝返回结果:%@", resultDic);
// 回调仅用于界面提示
// 应继续请求业务服务端查询订单状态
[self queryOrderStatusFromServer];
}];
Swift 项目可以调用 SDK 暴露的 Objective-C 接口:
let orderString = response.data.orderString
AlipaySDK.defaultService().payOrder(
orderString,
fromScheme: "your_app_scheme"
) { result in
print("支付宝返回结果:\(String(describing: result))")
self.queryOrderStatusFromServer()
}
如果支付宝可以正常打开,却无法返回 App,先检查以下几项:
fromScheme是否与项目注册的 URL Scheme 完全一致。- Scheme 的拼写和大小写是否正确。
- App 生命周期回调是否正确交给支付宝 SDK 处理。
- 使用多个 Scene 的项目是否遗漏了相应的 URL 回调入口。
具体回调代码取决于工程是否使用 SceneDelegate,请按照当前支付宝 iOS SDK 的接入说明进行配置。
如何快速走通完整流程
1. 先打通服务端下单接口
准备一个最小接口,例如:
POST /api/pay/alipay/create
Content-Type: application/json
请求:
{
"productId": "TEST_001"
}
订单号和金额由服务端生成,响应如下:
{
"code": 0,
"data": {
"orderNo": "202609130001",
"orderString": "..."
}
}
不要直接信任 App 上传的金额。正式业务应根据 productId 在服务端查询实际价格。
2. 在控制台或 HTTP 工具中检查返回值
主要检查:
orderString是否为空。method是否对应 App 支付接口。app_id是否与测试环境的配置一致。sign_type、字符集和签名配置是否一致。biz_content是否包含唯一的out_trade_no。total_amount和product_code是否符合接口要求。- 支付宝服务器能否通过公网访问
notify_url。
控制台调用成功,只能说明服务端的请求参数和签名基本可用。客户端 SDK、URL Scheme 和异步通知等环节仍需单独验证。
3. 在测试 App 中只保留必要流程
测试页面可以只放一个按钮:
点击按钮
→ 请求 /api/pay/alipay/create
→ 取得 orderString
→ 调用支付宝 SDK
→ 支付结束后查询订单状态
暂时不要加入优惠券、复杂订单合并和页面跳转等业务逻辑。等最小流程跑通后,再逐步恢复正式逻辑,这样更容易找到问题所在。
4. 使用匹配的测试环境
使用支付宝沙箱时,通常需要保证以下配置来自同一套沙箱环境:
- 沙箱应用的
app_id - 沙箱对应的密钥
- 沙箱网关
- 沙箱买家账号
- 符合沙箱要求的客户端或测试方式
沙箱参数不能与正式环境参数混用。沙箱工具和支持方式可能调整,具体配置应以当前支付宝开放平台控制台显示的内容为准。
5. 验证异步通知
支付完成后,支付宝会请求服务端的 notify_url。服务端需要:
- 读取支付宝发送的通知参数。
- 使用支付宝公钥验签。
- 校验
app_id、商户身份、订单号和金额。 - 查询本地订单,确认该订单尚未处理。
- 在事务中更新支付状态。
- 按接口要求返回成功响应。
伪代码如下:
if (!verifyAlipaySignature(requestParams)) {
return "failure";
}
Order order = orderService.findByOrderNo(outTradeNo);
if (order == null) {
return "failure";
}
if (!order.getAmount().equals(notifyAmount)) {
return "failure";
}
if (!expectedAppId.equals(notifyAppId)) {
return "failure";
}
orderService.markPaidIdempotently(order, tradeNo);
return "success";
支付宝可能重复发送异步通知,因此订单更新必须具备幂等性。同一个支付宝交易号或商户订单号不能重复入账或重复发货。
常见问题排查
SDK 提示参数错误
先确认传给 SDK 的内容确实是完整的 orderString,而不是以下内容:
- 整个 JSON 响应;
- 单独的
biz_content; - 被截断的字符串;
- App 自行重新拼接的参数;
- 经过错误 URL 解码或二次编码的字符串。
网络库在解析 JSON 时通常会正确还原转义字符,业务代码不要再随意调用 URLDecoder、decodeURIComponent,也不要重新编码签名字段。
能拉起支付宝,但支付失败
优先核对:
- 应用 ID、商户密钥和支付宝公钥是否配套。
- 当前使用的是正式网关还是沙箱网关。
- 订单号是否重复。
- 金额格式是否正确。
- 签名算法是否与配置一致。
- 应用是否拥有对应的产品权限。
- 商户或应用当前是否允许交易。
服务端日志应完整记录支付宝返回的错误码和子错误码,不能只留下“支付失败”四个字。
App 显示成功,但服务端订单未支付
客户端 SDK 返回的结果不能作为发货依据。常见原因包括:
notify_url无法从公网访问;- HTTPS 证书或网络策略异常;
- 服务端验签时使用了错误的公钥;
- 通知参数在验签前已被修改;
- 金额或应用 ID 校验失败;
- 数据库更新异常;
- App 查询订单状态的时间过早。
支付回跳后,客户端可以短暂轮询业务服务端:
async function waitForPayment(orderNo) {
for (let i = 0; i < 5; i++) {
const result = await queryOrder(orderNo);
if (result.status === "PAID") {
return result;
}
await new Promise(resolve => setTimeout(resolve, 1500));
}
return { status: "PROCESSING" };
}
轮询次数和间隔要根据实际业务设置,不能无限请求。
注意事项
- 商户私钥和签名逻辑必须放在服务端。
- App 只接收和使用服务端生成的订单字符串。
- 不要记录完整私钥、身份证号、手机号等敏感信息。
- 测试日志可以记录订单号、支付宝交易号、错误码和主要处理阶段。
- 每次调试都使用新的
out_trade_no,避免重复订单干扰判断。 - 客户端同步结果只用于界面提示。订单处理应以服务端异步通知和主动查询结果为依据。
- 收到支付成功通知后,仍要校验订单号、金额、应用和商户身份。
- 正式上线前,需要测试支付成功、用户取消、支付处理中、重复通知、网络中断和回跳失败等情况。
备注:内容仅供参考。
