支付宝集成
本文档介绍 HealthAide(健康助手)App 中支付宝能力的完整集成方式,涵盖支付功能(基于支付宝官方 alipaysdk-android SDK)与支付宝激活功能(基于阿里巴巴 AliAgent IoT 代理 SDK 的 ALiIOTKit 模块),包括依赖配置、Manifest 声明、结果解析模型与状态码约定。
Purpose and Scope
本页面向开发与集成人员,说明 JL_Health 工程中支付宝相关代码的真实实现:
- 支付宝支付 SDK 的引入方式与 Android 11+ 应用可见性(
<queries>)声明; - 支付结果回调 Activity
AlipayResultActivity的注册与 scheme 约定; - 支付结果模型
ALiPayResult/ 基类PayResult的解析逻辑与状态码含义; - 支付宝激活能力:
ALiIOTKit通过AliAgentSdk建立手表与阿里 IoT 平台的连接,使手表具备支付宝离线支付能力。
不在本页范围内的内容:设备连接与查找(见「设备管理」相关页面)、微信支付(WeixinPayResult 所在页面)、手表端支付逻辑(由固件/厂商 SDK 提供)。本页聚焦 App 侧与支付宝相关的集成代码。
Overview
根据仓库 README 的功能矩阵,支付宝是设备管理能力的一部分,其价值是「支付宝激活、支付功能」——即既能让用户通过手机 App 为设备开通支付宝(激活),也承载 App 内通过支付宝完成的支付流程。工程中对应两条技术路径:
- 支付路径:通过官方
com.alipay.sdk:alipaysdk-android:+@aar发起支付,支付宝 App 或 H5 收银台完成后,SDK 通过AlipayResultActivity回调结果Map<String, String>,由ALiPayResult解析为标准化的PayResult。 - 激活路径:通过
libs/ALi/AliAgent-release-xxx.aar(阿里巴巴 AliAgent SDK)建立设备(手表)到阿里 IoT 平台的通道,App 侧由ALiIOTKit封装初始化、自定义蓝牙数据传输与连接状态回调,用于完成手表的支付宝开通/激活流程。
两条路径共享同一套支付结果语义(9000 成功、6001 取消等),业务层统一通过 PayResult 子类消费结果,屏蔽了支付宝 SDK 的细节。
Architecture
flowchart TD
subgraph sg_App["HealthAide App (com.jieli.healthaide)"]
UI["设备市场 / 支付 UI"]
PAYBEAN["ALiPayResult (extends PayResult)"]
PAYRESULT["PayResult 基类"]
IOTKIT["ALiIOTKit (工具类)"]
ENV["HealthApplication (EnvUtils)"]
end
subgraph sg_SDK["第三方 SDK 层"]
ALIPAYSDK["alipaysdk-android (com.alipay.sdk)"]
ALIAGENT["AliAgentSdk (libs/ALi/AliAgent-release.aar)"]
end
subgraph sg_EXT["外部系统"]
ALIPAYAPP["支付宝 App / 收银台"]
ALIIOT["阿里 IoT 平台 (LP 通道)"]
WATCH["JL 手表设备 (蓝牙)"]
end
UI --> PAYBEAN
UI --> IOTKIT
PAYBEAN --> PAYRESULT
ENV --> ALIPAYSDK
ALIPAYSDK --> PAYBEAN
ALIPAYSDK --> ALIPAYAPP
IOTKIT --> ALIAGENT
ALIAGENT --> ALIIOT
IOTKIT --> WATCH
架构说明:
ALiPayResult(ALiPayResult.java)是支付结果的唯一入口模型:构造时接收支付宝 SDK 返回的Map<String, String>,从中提取resultStatus/result/memo三个键,并依据resultStatus是否为9000将统一结果码置为0(成功)或1(失败)。它继承自PayResult,以PAY_WAY_ALI标识支付方式,使上层 UI 无需区分支付宝/微信等渠道。ALiIOTKit(ALiIOTKit.java)是激活路径的门面类:持有IAliAgent代理实现,通过AliAgentSdk.getInstance()初始化,注册自定义蓝牙数据传输(customBtImpl)与 LP 连接状态回调(setLpConnectedCallback),并在设备连接事件中检查 FGS 状态。EnvUtils在HealthApplication中被引用,用于在 App 启动阶段配置支付宝 SDK 的运行环境(沙箱/线上)。
支付集成实现
依赖引入
支付宝官方 SDK 在 app/build.gradle 中以 AAR 形式引入,版本使用 + 通配符跟随最新发布:
//支付宝SDK
implementation 'com.alipay.sdk:alipaysdk-android:+@aar'
Source: app/build.gradle
使用 + 的好处是持续获得 SDK 修复;风险是构建结果不可复现,生产工程通常建议锁定具体版本号。此外,激活能力所需的 AliAgent 库以本地 AAR 形式存放于 libs/ALi/(见 README.md)。
Android 11+ 应用可见性声明
支付宝 SDK 需要探测设备是否安装了支付宝 App 以决定跳转方式(唤起 App 或 H5 收银台)。当 targetSdkVersion >= 30 时,Android 的包可见性机制要求显式声明。工程在 AndroidManifest.xml 中同时声明了大陆版支付宝与 AlipayHK:
<queries>
<package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 -->
<package android:name="hk.alipay.wallet" /> <!-- AlipayHK -->
</queries>
Source: AndroidManifest.xml
结果回调 Activity 注册
为了使用支付宝 SDK 的「通用跳转 SDK」能力,Manifest 中注册了 SDK 自带的 AlipayResultActivity,并为其配置了 scheme 为 __lsalipaysdk__ 的 VIEW intent-filter,且显式使用 tools:node="merge" / tools:node="replace" 处理与库 Manifest 的合并冲突:
<activity
android:name="com.alipay.sdk.app.AlipayResultActivity"
android:enabled="true"
android:exported="true"
tools:node="merge">
<intent-filter tools:node="replace">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="__lsalipaysdk__" />
</intent-filter>
</activity>
Source: AndroidManifest.xml
设计意图:AlipayResultActivity 由 SDK 提供,exported="true" 允许支付宝 App 通过 scheme 回跳;tools:node 指令确保三方库 Manifest 中的同名声明与本声明合并(merge)或整体替换(replace),避免构建期冲突。
支付结果解析模型
ALiPayResult 是支付宝结果的标准化封装,定义在 ALiPayResult.java。其核心逻辑:构造时遍历 SDK 回调的 Map<String, String>,按 resultStatus / result / memo 三个键取值,再依据 isOk() 将统一业务码置为 0(成功)或 1(失败),并将 memo 写入 message:
public ALiPayResult(Map<String, String> rawResult) {
super(PayResult.PAY_WAY_ALI);
this.rawResult = rawResult;
if (rawResult == null) return;
for (String key : rawResult.keySet()) {
if (TextUtils.equals(key, KEY_RESULT_STATUS)) {
resultStatus = rawResult.get(key);
} else if (TextUtils.equals(key, KEY_RESULT)) {
result = rawResult.get(key);
} else if (TextUtils.equals(key, KEY_MEMO)) {
memo = rawResult.get(key);
}
}
setCode(isOk() ? 0 : 1);
setMessage(memo);
}
public boolean isOk() {
return TextUtils.equals(resultStatus, PAY_OK);
}
Source: [ALiPayResult.java](https://gitee.com/Jieli-Tech/Android-JL_Health/blob/main/code/app/HealthAide_V1.1.0_SDK_V1.14.0/app/src/main/java/com/jieli/healthaide/ui/device/market/bean/ALiPayResult.java#L25-L40, L73-L75)
设计意图:支付宝 SDK 的原始结果是一个松散定义的 Map,且 resultStatus 为字符串形式的业务码。ALiPayResult 在构造期一次性完成解析与归一化,把「成功与否」收敛为 isOk() 一个布尔判断;同时保留 rawResult、getResult()、getMemo() 等原始字段供上层做更精细的展示与排查(例如「支付中需查询商家服务器」这类中间状态)。toString() 的覆写也为日志打印提供了可直接阅读的结果摘要。
支付状态码约定
ALiPayResult 中集中定义了支付宝 SDK 的标准返回码常量,见 ALiPayResult.java:
| 常量 | 值 | 含义 | 业务处理建议 |
|---|---|---|---|
PAY_OK | 9000 | 支付成功 | 仅此状态应触发成功分支(isOk() 返回 true) |
PAY_PROCESSING | 8000 | 正在处理中 | 需要查询商家服务器确认最终支付状态 |
PAY_FAILED | 4000 | 支付失败 | 提示用户支付失败,可重试 |
PAY_DUPLEX | 5000 | 重复请求 | 每次请求间隔需超过 3 秒 |
PAY_USE_CANCEL | 6001 | 用户中途取消 | 无需告警,静默处理 |
PAY_NETWORK_EXCEPTION | 6002 | 网络连接出错 | 提示检查网络后重试 |
PAY_UNKNOWN_STATUS | 6004 | 支付结果未知 | 需要查询商家服务器确认最终支付状态 |
值得注意的设计细节:只有 9000 被 isOk() 视为成功,8000 与 6004 这类「结果未知」状态被归入失败(setCode(1)),但保留在 resultStatus 字段中,提示调用方应主动向商家服务器发起订单状态查询,而不是仅凭本地回调下结论——这是移动支付集成中避免「伪失败/伪成功」的标准做法。
支付宝激活(ALiIOTKit)
模块职责
ALiIOTKit(ALiIOTKit.java)封装了阿里巴巴 AliAgentSdk(存放于 libs/ALi/AliAgent-release-xxx.aar),其目标是让 JL 手表通过 App 中继接入阿里 IoT 平台,从而支持支付宝的开通/激活能力。核心成员:
mAliAgent(IAliAgent):阿里代理实现类,所有 IoT 操作的入口;mWatchManager:手表管理类,用于监听设备连接事件以触发 IoT 通道建立。
初始化与连接
初始化发生在非 RCSP 测试模式下:先通过单例获取 AliAgentSdk,再以 Application 上下文与 BuildConfig.DEBUG(同时作为日志开关)调用 init,最后取出代理实例并注册自定义蓝牙传输与 LP 连接回调:
AliAgentSdk aliAgentSdk = AliAgentSdk.getInstance();
if (testMode != TEST_MODE_RCSP_TEST) { //init ALi agent
aliAgentSdk.init(HealthApplication.getAppViewModel().getApplication(), BuildConfig.DEBUG, BuildConfig.DEBUG);
mAliAgent = aliAgentSdk.getAgent(); //阿里代理实现类
//设置自定义数据处理实现
mAliAgent.customBtImpl(new IJustDataTransportAliBt() { ... });
//注册服务器状态回调
mAliAgent.setLpConnectedCallback(new IConnectCallback() { ... });
}
Source: ALiIOTKit.java
关键设计:
customBtImpl(IJustDataTransportAliBt):AliAgent 默认走自家蓝牙协议,而 JL 手表使用自有 BLE 通道;此接口让 App 把「阿里 IoT 报文」映射到「JL 蓝牙通道」上,实现数据面复用,避免双蓝牙连接冲突。setLpConnectedCallback(IConnectCallback):注册 LP(Long-Polling/长连接)服务器状态回调,App 据此感知 IoT 通道的断开/恢复,进而驱动重连或提示。testMode != TEST_MODE_RCSP_TEST守卫:在 RCSP(设备生产测试)模式下跳过阿里代理初始化,避免测试流程污染线上 IoT 通道。- 初始化参数使用
BuildConfig.DEBUG:同一代码在 debug/release 构建下自动切换日志与沙箱行为,无需手工维护开关。
设备连接联动
当手表通过 WatchManager 建立连接后,dealWithDeviceConnectedEvent() 会检查 FGS(可能指 Fine-Grained Service / 前台服务或安全状态检查)状态(checkFgsState,ALiIOTKit.java)。这一联动的目的:IoT 通道的建立依赖设备在线,因此必须在设备连接事件之后、且在代理实例有效(null == mAliAgent 守卫)的前提下执行,否则直接跳过。
Core Flow
支付流程
sequenceDiagram
participant UI as 设备市场/支付 UI
participant AS as alipaysdk-android
participant AA as 支付宝 App / 收银台
participant PR as ALiPayResult
participant BIZ as 业务层 (PayResult)
UI->>AS: 发起支付 (PayTask.pay)
AS->>AA: 唤起支付宝 / 跳转收银台
AA-->>AS: 支付完成,scheme 回跳 AlipayResultActivity
AS-->>UI: 回调 Map<String,String> {resultStatus, result, memo}
UI->>PR: new ALiPayResult(rawResult)
PR->>PR: 解析三键 / isOk() / setCode(0|1)
PR-->>BIZ: PayResult (PAY_WAY_ALI)
BIZ-->>UI: 按 resultStatus 分支处理 (9000/6001/8000...)
支付结果回调是一次同步本地解析:支付宝 SDK 通过已注册的 AlipayResultActivity 接收回跳,把结果以 Map 形式交回调用方;ALiPayResult 构造器内完成解析,业务层随后按上表状态码分支处理。对于 8000/6004,业务层应额外发起商家服务器订单查询。
激活流程
sequenceDiagram
participant WM as WatchManager
participant IK as ALiIOTKit
participant AG as AliAgentSdk / IAliAgent
participant IOT as 阿里 IoT 平台 (LP)
participant W as JL 手表 (BLE)
IK->>AG: AliAgentSdk.getInstance().init(ctx, DEBUG, DEBUG)
IK->>AG: customBtImpl(IJustDataTransportAliBt) 数据面映射
IK->>AG: setLpConnectedCallback(IConnectCallback)
WM-->>IK: 设备连接事件
IK->>AG: checkFgsState (设备在线校验)
AG->>IOT: 建立 LP 长连接
IOT-->>AG: 连接状态回调
AG->>W: 经自定义蓝牙通道下发激活/支付配置
W-->>AG: 上行数据 (IJustDataTransportAliBt)
激活流程的核心矛盾是「阿里 IoT 协议」与「JL 自有 BLE 协议」的差异:ALiIOTKit 通过 customBtImpl 做协议适配,使 AliAgent 无需感知 JL 蓝牙栈的具体实现;同时以设备连接事件为触发点,保证 IoT 会话建立在设备在线的前提之上。
Usage Examples
示例 1:解析支付宝支付结果
设备市场(Market)相关页面收到支付宝 SDK 回调后,直接构造 ALiPayResult 即可获得统一的 PayResult 语义(成功码、提示语、支付方式标识):
// 支付宝 SDK 回调的原始结果
Map<String, String> rawResult = ...;
ALiPayResult result = new ALiPayResult(rawResult);
if (result.isOk()) {
// resultStatus == "9000",支付成功
} else {
// 失败/取消/处理中,可通过 result.getResultStatus() 细分
}
Source: ALiPayResult.java
ALiPayResult 还保留了 toString() 的日志友好输出(resultStatus={...};memo={...};result={...}),便于在支付失败时随日志上报现场信息。
示例 2:日志打印结果摘要
@Override
public String toString() {
return "resultStatus={" + resultStatus + "};memo={" + memo
+ "};result={" + result + "}";
}
Source: ALiPayResult.java
示例 3:初始化 AliAgent 激活通道
激活模块在满足非 RCSP 测试模式时初始化 AliAgent,并注册自定义蓝牙数据通道与 LP 连接回调(完整代码见 ALiIOTKit.java):
AliAgentSdk aliAgentSdk = AliAgentSdk.getInstance();
if (testMode != TEST_MODE_RCSP_TEST) { //init ALi agent
aliAgentSdk.init(HealthApplication.getAppViewModel().getApplication(), BuildConfig.DEBUG, BuildConfig.DEBUG);
mAliAgent = aliAgentSdk.getAgent(); //阿里代理实现类
mAliAgent.customBtImpl(new IJustDataTransportAliBt() { ... }); //数据面映射
mAliAgent.setLpConnectedCallback(new IConnectCallback() { ... }); //服务器状态回调
}
Source: ALiIOTKit.java
Configuration Options
支付宝集成相关的配置分散在构建脚本与 Manifest 中,均为构建期/声明期配置,无运行时动态配置项:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
com.alipay.sdk:alipaysdk-android 版本 | Gradle 依赖 | +(跟随最新) | 支付宝官方支付 SDK |
AliAgent-release-xxx.aar | 本地 AAR(libs/ALi/) | 随版本更新 | 支付宝激活 SDK(AliAgent) |
<queries> 包名 | Manifest | com.eg.android.AlipayGphone、hk.alipay.wallet | Android 11+ 应用可见性声明 |
AlipayResultActivity | Manifest Activity | exported=true,scheme=__lsalipaysdk__ | 通用跳转 SDK 回跳入口 |
BuildConfig.DEBUG(init 参数) | 构建期布尔 | 随构建类型 | 同时作为 AliAgent 日志/沙箱开关 |
TEST_MODE_RCSP_TEST | 常量守卫 | 非 RCSP 时初始化 | RCSP 测试模式下跳过 AliAgent 初始化 |
API Reference
class ALiPayResult extends PayResult
支付宝支付结果模型。构造即解析,详见 ALiPayResult.java。
构造方法:
ALiPayResult(Map<String, String> rawResult):接收支付宝 SDK 原始回调 Map。内部以PAY_WAY_ALI标识支付方式,提取resultStatus/result/memo,并根据isOk()设置统一码(0 成功 / 1 失败)与消息(memo)。传入null时安全返回(各字段保持 null)。
常量:
| 常量 | 值 | 说明 |
|---|---|---|
PAY_OK | "9000" | 支付成功 |
PAY_PROCESSING | "8000" | 正在处理中(需查询商家服务器) |
PAY_FAILED | "4000" | 支付失败 |
PAY_DUPLEX | "5000" | 重复请求(间隔需超过 3 秒) |
PAY_USE_CANCEL | "6001" | 用户中途取消 |
PAY_NETWORK_EXCEPTION | "6002" | 网络连接出错 |
PAY_UNKNOWN_STATUS | "6004" | 支付结果未知(需查询商家服务器) |
方法:
boolean isOk():返回true当且仅当resultStatus等于PAY_OK("9000")。这是业务层判断成功与否的唯一依据。Map<String, String> getRawResult():返回构造时传入的原始结果 Map(可能为 null),供排查或透传。String getResultStatus():返回原始状态码字符串(如"9000"、"6001")。String getMemo():返回 SDK 附带的中文提示信息。String getResult():返回结果体字符串(支付宝签名结果,可用于服务端验签)。String toString():返回resultStatus={...};memo={...};result={...}格式的日志摘要。
继承自 PayResult 的字段/方法(由子类调用):
super(PayResult.PAY_WAY_ALI):在构造首行调用,声明支付方式为支付宝;setCode(int)/setMessage(String):写入统一业务码与提示语,供上层PayResult统一消费。
Failure Modes, Edge Cases & Concurrency
状态码边界(伪成功/伪失败)
8000与6004不是成功也不是纯失败:isOk()对二者返回false,业务码为1,但resultStatus保留原值。上层若仅按isOk()提示「支付失败」会误导用户——此时订单可能实际已扣款。正确做法是检测到这两个状态时向商家服务器发起订单查询(代码注释明确标注「需查询商家服务器」)。5000重复请求:SDK 约定两次请求间隔需超过 3 秒。快速连点支付按钮会触发该码,UI 层应做按钮防抖或对PAY_DUPLEX做静默降级处理。6001用户取消:属于用户主动行为,不应弹错误提示;ALiPayResult仍将其归为失败码1,因此业务层需按resultStatus细分提示文案,而不是统一「支付失败」。
空结果与异常输入
ALiPayResult构造器对rawResult == null做了防御(if (rawResult == null) return;),此时resultStatus/result/memo均为 null,isOk()因TextUtils.equals(null, "9000")返回 false——即空回调被安全地视为失败,不会抛 NPE。- 若 Map 中缺少
resultStatus键,同样导致isOk()为 false,语义上等同于「未知结果」,符合安全默认。
并发与重入
- 支付结果解析是纯内存、无共享可变状态的操作:
ALiPayResult每个实例独立持有解析结果,多线程/多支付单并发回调互不干扰。 ALiIOTKit的mAliAgent在初始化分支中赋值、在非初始化分支置 null(见 ALiIOTKit.java),dealWithDeviceConnectedEvent()使用null == mAliAgent守卫避免空指针——但该字段的读写未加锁,若设备连接事件与初始化在不同线程发生,理论上存在可见性窗口;实际中初始化通常在启动阶段完成,风险较低。
激活通道的失败路径
- LP 连接断开:通过
setLpConnectedCallback的IConnectCallback回调感知,由调用方决定重连策略; - 设备离线:IoT 通道建立在设备连接事件之后(
dealWithDeviceConnectedEvent→checkFgsState),设备断开时数据上行自然失败,需等待下一次连接事件重新触发; - RCSP 测试模式:
testMode == TEST_MODE_RCSP_TEST时跳过 AliAgent 初始化,任何激活相关调用在测试模式下均不可用——这是刻意的隔离,防止生产 IoT 通道被测试流程干扰。
Performance & Operational Considerations
- 支付结果解析零开销:
ALiPayResult仅在构造时遍历一次 Map(键数量固定为 3),无网络、无磁盘 IO,可在 UI 线程安全执行。 alipaysdk-android版本使用+:每次构建都会解析最新 AAR,可能引入未经回归验证的 SDK 行为变化。建议在发版前锁定版本号,并将升级作为独立变更提交。- AliAgent 数据面:支付宝激活/支付配置数据经 JL 自有 BLE 通道传输(
IJustDataTransportAliBt),BLE 吞吐有限,大报文(如签名、证书)可能耗时较长;调试时可通过BuildConfig.DEBUG开关观察 AliAgent 内部日志。 - 日志友好性:
ALiPayResult.toString()设计为一行式摘要,建议在支付失败分支直接打日志,便于线上问题复现(配合商家服务器订单查询)。
Extension Points
- 新增支付渠道:
ALiPayResult与微信等渠道共用PayResult基类(PAY_WAY_ALI标识)。新增渠道时仿照本类实现一个PayResult子类即可接入现有 UI 流程,无需改动业务层消费逻辑。 - 自定义蓝牙数据面:
IJustDataTransportAliBt是 AliAgent 与 JL 蓝牙栈之间的适配接口,更换蓝牙协议栈(如蓝牙 5.0 LE Audio、双模)时只需重写该实现,ALiIOTKit的初始化与回调注册保持不变。 - 沙箱/环境切换:
EnvUtils(com.alipay.sdk.app.EnvUtils)被HealthApplication引用,可用于在联调环境切换支付宝沙箱;AliAgent 侧则通过BuildConfig.DEBUG控制调试行为。 - 结果状态扩展:若需支持新的支付宝状态码(如未来新增的中间态),在
ALiPayResult中追加常量并调整isOk()/业务分支即可,状态码常量集中定义便于一处修改、全局生效。
Tests
源码中未发现针对支付宝集成(ALiPayResult / ALiIOTKit)的独立单元测试文件(本次检索范围内无对应 test 源文件)。ALiPayResult 的纯解析逻辑(无 Android 依赖,仅使用 android.text.TextUtils)具备良好的可测性,建议补充覆盖以下用例:
rawResult == null时isOk()为 false、无异常;resultStatus = "9000"时isOk()为 true、code == 0;resultStatus为8000/4000/6001/6002/6004时isOk()为 false、code == 1;- Map 缺少
memo/result键时的空值行为。
Related Links
- PayResult 基类(支付结果统一模型) — 支付宝/微信支付结果统一消费入口(如仓库中存在对应目录)
- 微信支付集成 — 另一支付渠道的实现对照(如仓库中存在对应目录)
- 设备管理 — 本页所属能力域
- ALiPayResult.java — 支付结果模型源码
- ALiIOTKit.java — 支付宝激活/IoT 通道封装源码
- [AndroidManifest.xml](https://gitee.com/Jieli-Tech/Android-JL_Health/blob/main/code/app/HealthAide_V1.1.0_SDK_V1.14.0/app/src/main/AndroidManifest.xml#L10-L13, L127-L141) — 支付宝 SDK 声明配置
- app/build.gradle — 支付宝 SDK 依赖
- [README.md](https://gitee.com/Jieli-Tech/Android-JL_Health/blob/main/README.md#L59, L171-L173) — 支付宝能力与 AliAgent 库说明