PAYATHON 2026

大陆支付宝SDK与香港支付宝SDK如何在同一APP中集成?

支付阿杰

结论

同一个 Android APP 可以同时支持大陆支付宝和香港支付宝,前提是两套支付能力能够在客户端共存。

如果 AlipayHK 的本地 JAR 与大陆支付宝 SDK 使用了相同的包名或类名,就不能直接把两个依赖放进同一个项目。exclude、调整依赖顺序或只保留其中一套重复类,通常无法可靠解决这类冲突,因为两个 SDK 可能依赖不同版本的内部实现。

建议按以下顺序处理:

  1. 向大陆支付宝和 AlipayHK 的技术支持确认,是否有支持共存的新版 SDK、统一 SDK,或已经更换包名的兼容版本。
  2. 如果没有可共存的版本,可以让一个渠道使用客户端 SDK,另一个渠道使用官方支持的 H5、网页收银台或其他不会引起依赖冲突的接入方式。
  3. 只有在官方明确允许,而且 SDK 不涉及复杂的反射、JNI、资源绑定等机制时,才考虑对 JAR 进行包名重定位。自行修改 SDK 风险很高,不适合作为正式项目的首选方案。
  4. productFlavors 只能生成分别支持不同支付宝的多个 APK,不能让同一个安装包同时支持两种支付。

大陆支付宝 APP SDK 通常不能直接拉起 AlipayHK APP。最终拉起哪个钱包,取决于接入的支付产品、客户端 SDK、订单类型和钱包侧协议,不能通过修改订单参数或包名来切换。如果需要接入 AlipayHK 支付,应采用 AlipayHK 或相应跨境支付产品提供的正式方案。

为什么会发生冲突

Android 构建 APK 时,同一个完整类名只能存在一份。例如,两个依赖都包含:

com.alipay.sdk.app.PayTask

构建时可能出现类似错误:

Duplicate class com.alipay.sdk.app.PayTask found in modules ...

即使构建工具没有立即报错,应用运行时也可能加载错误的版本,进而出现以下问题:

  • NoSuchMethodError
  • ClassNotFoundException
  • 支付页面无法拉起
  • 支付结果解析异常
  • 回调失败
  • 混淆后出现兼容性问题

这不是普通的 Gradle 版本仲裁问题。对于包含相同类名的本地 JAR,Gradle 无法像处理常规 Maven 版本冲突那样,自动选出一套同时兼容两个支付渠道的实现。

推荐的集成步骤

1. 先确认接入产品,而不只是 SDK 名称

需要分别向两个支付渠道确认:

  • 当前商户签约的是大陆支付宝、AlipayHK,还是某种跨境或聚合支付产品;
  • Android 端推荐使用哪个 SDK;
  • 最新 SDK 是否支持与另一套支付宝 SDK 共存;
  • 是否提供 Maven 坐标,以免继续使用过期的本地 JAR;
  • 是否支持 H5 收银台;
  • SDK 实际会拉起哪个钱包 APP;
  • 回跳协议、异步通知和订单查询分别使用什么接口。

不同商户产品的接入方式可能并不相同,不能因为名称中都有“支付宝”,就认为两边的订单或 SDK 可以通用。

2. 检查重复类

将两个 SDK 放入测试分支后,可以使用 Android Studio 的依赖分析功能查看依赖来源,也可以直接检查 JAR 中的类列表。例如:

jar tf alipayhk-sdk.jar

检查时需要注意两套依赖中是否存在相同的完整类名、资源名,以及重复的 provider、activity 或 service 声明。

如果已经出现 Duplicate class,不要通过删除不确定用途的类文件来强行通过编译。应先向官方索取兼容版本。

3. 在业务层隔离两个支付渠道

无论最终采用两套 SDK,还是“一套 SDK + 一套 H5”,业务代码都不应到处直接调用具体 SDK。可以先定义统一的支付接口:

enum class PaymentChannel {
    ALIPAY_CN,
    ALIPAY_HK
}

data class PaymentRequest(
    val channel: PaymentChannel,
    val orderInfo: String,
    val cashierUrl: String? = null
)

sealed class PaymentResult {
    data class Success(val rawResult: String) : PaymentResult()
    data class Pending(val rawResult: String) : PaymentResult()
    data class Failure(
        val code: String?,
        val message: String?,
        val rawResult: String?
    ) : PaymentResult()
}

interface PaymentAdapter {
    suspend fun pay(request: PaymentRequest): PaymentResult
}

大陆支付宝适配器可以封装官方 PayTask。具体调用方式应以项目所用版本的官方文档为准:

class AlipayCnAdapter(
    private val activity: Activity
) : PaymentAdapter {

    override suspend fun pay(request: PaymentRequest): PaymentResult {
        require(request.channel == PaymentChannel.ALIPAY_CN)

        return withContext(Dispatchers.IO) {
            val payTask = PayTask(activity)
            val result = payTask.payV2(request.orderInfo, true)

            PaymentResult.Pending(
                rawResult = result.toString()
            )
        }
    }
}

这里返回 Pending,是因为客户端返回值不能作为最终入账依据。实际项目可以继续解析 resultStatus 来更新页面状态,但最终支付结果仍应由服务端确认。

香港支付宝适配器需要根据最终获批的接入方式实现。如果采用 H5,也可以继续使用同一套接口:

class AlipayHkH5Adapter(
    private val activity: Activity
) : PaymentAdapter {

    override suspend fun pay(request: PaymentRequest): PaymentResult {
        require(request.channel == PaymentChannel.ALIPAY_HK)

        val url = requireNotNull(request.cashierUrl) {
            "AlipayHK cashierUrl is required"
        }

        withContext(Dispatchers.Main) {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
            activity.startActivity(intent)
        }

        return PaymentResult.Pending(rawResult = "H5 cashier opened")
    }
}

这段代码只用于说明客户端架构和渠道隔离方式。H5 是否允许在外部浏览器中打开、是否必须使用 WebView,以及回跳 URL 的具体格式,都应以相应支付产品的正式文档为准。

4. 后端分别创建订单

客户端不应自行拼接支付参数或完成签名。服务端应根据支付渠道调用对应的接口:

{
  "orderId": "ORDER_202609090001",
  "channel": "ALIPAY_HK",
  "orderInfo": null,
  "cashierUrl": "https://example.invalid/cashier/session-id"
}

客户端再根据 channel 选择适配器:

suspend fun startPayment(request: PaymentRequest): PaymentResult {
    val adapter = when (request.channel) {
        PaymentChannel.ALIPAY_CN -> alipayCnAdapter
        PaymentChannel.ALIPAY_HK -> alipayHkAdapter
    }

    return adapter.pay(request)
}

两种支付渠道需要分别维护:

  • 商户号及应用身份;
  • 私钥、公钥或证书;
  • 签名算法及验签逻辑;
  • 下单接口;
  • 异步通知地址;
  • 退款和查询接口;
  • 订单号与渠道交易号;
  • 支付状态映射。

不要把大陆支付宝生成的 orderInfo 直接传给香港支付宝 SDK,也不要假设双方的签名方式、订单字段和返回码完全相同。

SDK 确实冲突时的处理方案

方案一:使用官方共存版本

这是风险最低的方案。共存版本通常会通过更换包名、统一底层组件或合并接口来解决重复类问题。

替换后需要覆盖以下测试场景:

  • 仅安装大陆支付宝 APP;
  • 仅安装 AlipayHK APP;
  • 两个钱包都安装;
  • 两个钱包都未安装;
  • 支付成功、取消和失败;
  • APP 被系统回收后的回跳;
  • 从支付完成页返回 APP;
  • 服务端异步通知晚于客户端回调;
  • 混淆后的正式包。

方案二:一个渠道使用 SDK,另一个渠道使用 H5

如果官方没有提供可共存的 SDK,通常可以让一个渠道使用 SDK,另一个渠道使用 H5。第二套冲突 JAR 不会被打入 APK,因此重复类问题也就不存在了。

采用这种方式前,需要确认对应产品正式支持 H5,并处理好以下事项:

  • 浏览器或 WebView 的使用限制;
  • 自定义 Scheme、App Link 或 Universal Link 回跳;
  • 用户取消支付;
  • 页面关闭后主动查询订单;
  • 防止伪造回跳参数;
  • 服务端异步通知验签。

方案三:对 JAR 做包名重定位

理论上,可以使用构建工具整体改写其中一套 SDK 的包名,例如将:

com.alipay.sdk.*

迁移为:

com.example.shaded.alipayhk.*

但只有同时满足以下条件,这种做法才可能可行:

  • SDK 授权条款允许修改或重新打包;
  • SDK 不依赖固定类名进行反射;
  • 不包含无法同步修改的 JNI 类名;
  • 不依赖固定包名的组件声明;
  • 不存在资源名称或 ContentProvider 冲突;
  • 官方愿意支持这种部署方式。

支付 SDK 涉及签名、风控和安全校验。自行重打包可能引起功能异常,也可能导致项目失去官方技术支持。没有支付方的书面确认,不建议在生产环境中使用这种方案。

不建议采用的做法

不要依赖下面这种方式解决冲突:

implementation(files("libs/alipayhk-sdk.jar")) {
    exclude(group = "com.alipay", module = "alipaySdk")
}

exclude 主要用于排除传递依赖,不能删除本地 JAR 中的部分重复类。

也不要简单保留“版本较新”的重复类。即使类名相同,两个支付渠道所需的方法、协议或内部实现也可能不同。项目能够编译,不代表运行时可靠。

把两套支付功能放进不同的 Android 进程同样无效。重复类问题发生在 APK 构建阶段,这些类仍需被打包到同一个应用中。

支付结果处理注意事项

客户端显示“支付成功”,并不表示资金已经可靠入账。正确流程如下:

  1. 客户端向业务服务端申请支付订单;
  2. 服务端调用对应渠道创建订单;
  3. 客户端通过对应 SDK 或收银台完成支付;
  4. 服务端接收并验证支付渠道的异步通知;
  5. 服务端主动查询订单,在必要时核对状态;
  6. 客户端向业务服务端查询最终订单状态。

客户端回调、H5 回跳参数和 SDK 返回字符串只能用来改善交互,不能作为发货或开通服务的唯一依据。

最终方案不应让大陆支付宝 SDK 去“兼容”或“冒充”AlipayHK SDK,而应将两个渠道作为独立的支付能力处理。优先获取官方可共存版本;如果没有,就采用官方支持的替代收银台方案,并在业务层和服务端分别隔离订单、签名、回调和状态处理。

备注:内容仅供参考。