PAYATHON 2026

SSLCOMMERZ 在 debug apk 正常但 release apk 无法使用

支付老李

结论

既然 flutter run --release 和 flutter run --profile 都能正常完成支付,SSLCOMMERZ 的基本集成通常没有问题,也不太可能只是缺少 INTERNET 权限。更值得检查的是,最终安装的 APK 与运行测试时使用的构建环境是否一致。

建议先检查以下几项:

  1. 安装的 APK 是否确实是刚生成的 release 包。
  2. release 签名、product flavor、环境变量和支付配置是否正确。
  3. R8/ProGuard 是否删除了 SDK 依赖的类。
  4. 支付回调涉及的 Activity、深链或网络安全配置是否只存在于 debug 配置中。
  5. 使用 adb logcat 查看已安装 APK 的 Android 日志,不要只看 Flutter 控制台。

先确认测试的是同一个 APK

flutter run --release 会构建并安装 release 版本。手动创建、复制或分发 APK 时,则可能误装旧文件、其他 flavor 的文件,或者 ABI 不匹配的文件。

先清理项目并重新构建:

flutter clean
flutter pub get
flutter build apk --release

默认产物通常位于:

build/app/outputs/flutter-apk/app-release.apk

卸载旧应用,再安装刚生成的 APK:

adb uninstall com.example.your_app
adb install build/app/outputs/flutter-apk/app-release.apk

将 com.example.your_app 替换为实际的 applicationId。卸载操作会清除应用数据。如果需要保留数据,可以先尝试:

adb install -r build/app/outputs/flutter-apk/app-release.apk

如果项目使用 flavor,构建和运行必须使用同一组参数,例如:

flutter run --release --flavor production -t lib/main_production.dart
flutter build apk --release --flavor production -t lib/main_production.dart

查看手动安装 APK 的日志

无论 APK 是否由 Flutter 命令启动,都可以通过 adb logcat 获取日志。连接手机并开启 USB 调试后,先确认设备已被识别:

adb devices

清空旧日志,然后启动应用并复现问题:

adb logcat -c
adb logcat

如果日志太多,可以按常见异常关键字过滤:

adb logcat | grep -iE "flutter|androidruntime|ssl|exception|fatal|chromium"

也可以只查看目标应用的进程日志。在 macOS 或 Linux 上执行:

adb logcat --pid=$(adb shell pidof -s com.example.your_app)

如果应用启动后立即退出,可能来不及获取进程 ID,此时可以使用:

adb logcat -b crash

重点查找以下内容:

  • FATAL EXCEPTION
  • ClassNotFoundException
  • NoClassDefFoundError
  • ActivityNotFoundException
  • SecurityException
  • NetworkSecurityPolicy
  • TLS、证书或域名校验错误
  • WebView、Chrome 或支付回调相关错误

如果系统中没有 adb,需要先安装 Android SDK Platform-Tools。

检查 AndroidManifest 配置

网络权限应写在能够合并到 release 构建的主清单中:

<!-- android/app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name="${applicationName}"
        android:label="your_app"
        android:icon="@mipmap/ic_launcher">
        <!-- Activities and other declarations -->
    </application>
</manifest>

不要只把权限写在以下文件中:

android/app/src/debug/AndroidManifest.xml
android/app/src/profile/AndroidManifest.xml

不过,Flutter 项目通常已经包含联网所需的配置。既然 flutter run --release 可以正常使用,单纯缺少 INTERNET 权限的可能性不高。

还可以检查最终 release 清单的合并结果。在 Android Studio 中打开 AndroidManifest.xml,切换到 Merged Manifest,确认 release 变体中包含所需的权限、Activity、intent filter 和 meta-data。

检查支付环境和构建参数

支付 SDK 的问题常常来自配置差异,而不是代码本身。例如,debug 运行时读取了测试配置,手动构建 APK 时却没有传入对应参数:

const storeId = String.fromEnvironment('STORE_ID');
const isSandbox = bool.fromEnvironment(
  'SSL_SANDBOX',
  defaultValue: true,
);

如果代码使用这种方式读取配置,运行和构建时必须传入相同的 --dart-define:

flutter build apk --release \
  --dart-define=STORE_ID=your_store_id \
  --dart-define=SSL_SANDBOX=true

对应的运行命令也要保持一致:

flutter run --release \
  --dart-define=STORE_ID=your_store_id \
  --dart-define=SSL_SANDBOX=true

同时核对以下内容:

  • sandbox 与 production 的凭据是否混用。
  • release APK 的 applicationId 是否发生变化。
  • 不同 flavor 是否使用了不同的配置文件。
  • 支付后台是否限制了包名、签名证书、回调地址或域名。
  • 构建脚本是否从本机环境变量读取密钥,而生成 APK 时该变量为空。

不要把正式商户密钥直接打印到日志中。

检查 release 签名差异

手动生成的 APK 可能使用 release keystore,而 flutter run 使用了不同的签名配置。如果支付服务、深链验证或相关平台通过证书指纹识别应用,签名不一致就可能导致支付失败。

查看 APK 的签名信息:

apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk

也可以检查 keystore 的证书指纹:

keytool -list -v -keystore /path/to/your-release-key.jks

不要在日志、截图或仓库中公开 keystore 密码和私钥文件。

排除 R8 或 ProGuard 优化问题

如果 release 构建启用了代码压缩,SDK 中通过反射、注解或字符串加载的类可能被 R8 删除。日志中通常会出现:

ClassNotFoundException
NoClassDefFoundError

检查 android/app/build.gradle 或 android/app/build.gradle.kts,确认 release 配置是否启用了:

minifyEnabled true
shrinkResources true

定位问题时,可以临时关闭这两项:

buildTypes {
    release {
        minifyEnabled false
        shrinkResources false
    }
}

如果关闭后恢复正常,就需要添加 keep rule,不建议长期关闭压缩。规则必须按照 SSLCOMMERZ Flutter 插件及其 Android SDK 实际使用的包名编写,例如:

-keep class actual.sdk.package.** { *; }
-dontwarn actual.sdk.package.**

actual.sdk.package 只是占位符,不能直接使用。应从插件源码、依赖列表或 SDK 文档中确认真实包名,不要根据名称猜测 keep rule。

检查网络安全与 TLS

如果日志中出现以下错误:

Cleartext HTTP traffic not permitted

说明某个请求仍在使用 http://。优先将接口和回调地址改为 https://。

只有当对方服务确实不支持 HTTPS,并且已经评估相关风险时,才考虑配置 networkSecurityConfig。不要为了排查问题,直接允许所有域名使用明文流量。

如果出现证书或 TLS 错误,应检查:

  • 手机的日期和时间是否正确。
  • 支付域名的证书链是否完整。
  • 旧 Android 设备是否支持服务端要求的 TLS 和证书。
  • 项目是否配置了证书固定,并且 release 使用了不同的证书。
  • WebView 或 Chrome 是否过旧。

给支付流程增加可观测日志

不要只记录“支付失败”,应分别记录支付初始化、页面打开、回调和异常阶段:

import 'dart:developer' as developer;

Future<void> startPayment() async {
  developer.log(
    'Starting SSLCOMMERZ payment',
    name: 'payment',
  );

  try {
    // 调用实际的 SSLCOMMERZ 支付 API。
    final result = await startSslCommerzPayment();

    developer.log(
      'Payment returned: $result',
      name: 'payment',
    );
  } catch (error, stackTrace) {
    developer.log(
      'SSLCOMMERZ payment failed',
      name: 'payment',
      error: error,
      stackTrace: stackTrace,
    );
  }
}

其中,startSslCommerzPayment() 需要替换为项目实际使用的 API。

日志中不要输出银行卡信息、交易密钥、完整 token、用户密码或其他敏感数据。正式发布时,更适合接入经过脱敏处理的崩溃和错误采集服务。

建议的排查顺序

按以下顺序处理通常更快:

  1. 执行 flutter clean,然后重新生成 APK。
  2. 卸载旧版本,直接安装刚生成的 app-release.apk。
  3. 使用 adb logcat 复现问题,找到第一条真实异常。
  4. 对比 flutter run --release 和 flutter build apk --release 使用的 flavor、入口文件及 --dart-define。
  5. 核对最终合并后的 Manifest。
  6. 检查 APK 签名,以及支付后台对包名和证书的限制。
  7. 如果日志中出现类缺失错误,再检查 R8/ProGuard。
  8. 如果出现 TLS、HTTP 或 WebView 错误,再处理网络安全配置。

首先要拿到 adb logcat 中的具体异常。由于 release 模式直接运行时支付正常,仅凭当前现象无法判断是 SSLCOMMERZ 插件缺陷。构建参数、安装包来源、签名或 flavor 不一致,更值得先查。

备注:内容仅供参考。