1. 支付宝SDK快速集成与沙箱支付
支付宝 App 支付的最小链路
支付宝 App 支付的核心是「客户端发起 → 服务端签名 → 客户端调起 SDK → 支付宝处理 → 同步/异步回调」这条链路。客户端不持有私钥,订单参数必须由服务端用应用私钥签名后返回,客户端只负责把签名后的订单串交给 SDK 调起收银台。
沙箱环境与生产环境的差异只在网关地址和密钥体系:沙箱使用独立的沙箱 AppID、沙箱买家账号和沙箱网关,签名算法与生产完全一致(推荐 RSA2)。因此沙箱跑通后,切换到生产只需替换 AppID、私钥和网关。
集成步骤与关键代码
在 app/build.gradle 中添加支付宝 SDK 依赖,并在 AndroidManifest.xml 中声明网络权限。SDK 通过 AAR 方式集成,无需额外配置混淆规则(SDK 自带 consumer-rules)。
// app/build.gradle
dependencies {
implementation 'com.alipay.sdk:alipaysdk-android:15.8.17'
}
// AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
服务端使用支付宝 Java SDK 生成签名订单串。沙箱环境需将网关切换为 https://openapi.alipaydev.com/gateway.do,并使用沙箱 AppID 与应用私钥。
// 服务端:生成 app_pay 订单串
AlipayClient client = new DefaultAlipayClient(
"https://openapi.alipaydev.com/gateway.do", // 沙箱网关
SANDBOX_APP_ID,
APP_PRIVATE_KEY, // 应用私钥(PKCS8)
"json", "UTF-8",
ALIPAY_PUBLIC_KEY, // 支付宝公钥,用于验签
"RSA2"
);
AlipayTradeAppPayRequest req = new AlipayTradeAppPayRequest();
req.setNotifyUrl("https://your-domain.com/alipay/notify");
JSONObject biz = new JSONObject();
biz.put("out_trade_no", "ORDER_" + System.currentTimeMillis());
biz.put("total_amount", "0.01");
biz.put("subject", "沙箱测试订单");
biz.put("product_code", "QUICK_MSECURITY_PAY");
req.setBizContent(biz.toJsonString());
// sdkExecute 返回已签名的 orderInfo 字符串
String orderInfo = client.sdkExecute(req).getBody();
客户端拿到 orderInfo 后,通过 PayTask.payV2() 调起收银台。注意 payV2 必须在子线程执行,结果通过 Message 回传主线程。
// 客户端:调起支付宝
public void pay(String orderInfo) {
Runnable payRunnable = () -> {
PayTask alipay = new PayTask(activity);
Map<String, String> result = alipay.payV2(orderInfo, true);
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
};
new Thread(payRunnable).start();
}
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == SDK_PAY_FLAG) {
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
String status = payResult.getResultStatus();
// 9000 支付成功;6001 用户取消;8000/6004 处理中
if ("9000".equals(status)) {
// 仅作 UI 提示,最终以服务端异步通知为准
showToast("支付成功,等待服务端确认");
} else if ("6001".equals(status)) {
showToast("用户取消支付");
} else {
showToast("支付结果待确认:" + status);
}
}
}
};
同步验签与结果判读
同步回调中的 result 字段是支付宝返回的签名数据,客户端可用支付宝公钥做本地验签,确认数据未被篡改。但同步回调不可作为发货依据——用户可能在回调前杀进程,或网络中断导致回调丢失。
// 客户端同步验签(可选,用于增强可信度)
public boolean verifySyncResult(String result) {
try {
return AlipaySignature.rsaCheckV1(
parseResultToMap(result),
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
} catch (AlipayApiException e) {
return false;
}
}
| resultStatus | 含义 | 处理建议 |
|---|---|---|
| 9000 | 支付成功 | 提示用户,等待服务端异步通知确认 |
| 6001 | 用户中途取消 | 恢复订单状态,允许重新支付 |
| 8000 / 6004 | 正在处理中 | 轮询服务端订单状态,不可重复发起 |
| 4000 / 5000 / 6002 | 失败 / 网络错误 | 提示重试,检查网络与订单参数 |
沙箱环境必须使用沙箱买家账号登录支付宝 App 才能完成付款。若使用真实账号,会提示「订单不存在」或「商户未开通」。沙箱买家账号在支付宝开放平台沙箱应用中查看。
服务端异步通知验签
异步通知是最终支付结果的权威来源。服务端收到 notify_url 的 POST 请求后,必须用支付宝公钥验签,并校验 out_trade_no、total_amount、app_id 与本地订单一致,再更新订单状态并返回 success。
// 服务端:异步通知验签
@PostMapping("/alipay/notify")
public String notify(HttpServletRequest request) throws Exception {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((k, v) -> params.put(k, v[0]));
// 1. 验签
boolean signOk = AlipaySignature.rsaCheckV1(
params, ALIPAY_PUBLIC_KEY, "UTF-8", "RSA2");
if (!signOk) return "failure";
// 2. 校验业务参数
String tradeStatus = params.get("trade_status");
String outTradeNo = params.get("out_trade_no");
String totalAmount = params.get("total_amount");
if ("TRADE_SUCCESS".equals(tradeStatus)) {
Order order = orderService.findByNo(outTradeNo);
if (order != null
&& order.getAmount().compareTo(new BigDecimal(totalAmount)) == 0) {
orderService.markPaid(outTradeNo, params.get("trade_no"));
}
}
return "success"; // 必须返回 success,否则支付宝会重试
}
沙箱调试时,
notify_url 必须是公网可访问地址。本地开发可用内网穿透工具映射到本地端口,或在沙箱控制台配置可访问的回调地址。异步通知的验签逻辑与生产完全一致,沙箱跑通即可直接复用。
最小验证清单
- 依赖集成后能编译通过,
PayTask类可正常引用。 - 服务端
sdkExecute返回的orderInfo非空且包含sign字段。 - 客户端调起后能打开支付宝沙箱收银台,显示订单金额与商品名。
- 使用沙箱买家账号付款后,
resultStatus返回9000。 - 服务端收到异步通知,验签通过,订单状态更新为已支付。
- 重复收到同一
out_trade_no的通知时,订单状态不重复变更(幂等)。