Flutter 集成 PhonePe Payment SDK 时出现 MissingPluginException
结论
MissingPluginException 说明 Dart 已经调用了 PhonePe 的 MethodChannel,但当前 FlutterEngine 没有注册对应的 Android 原生插件。这个异常通常与请求参数、checksum 或 Maven 仓库下载无关。
建议按以下顺序排查:
- 确认应用运行在 Android 真机或 Android 模拟器上,而不是 Web、桌面端或普通单元测试环境。
- 添加插件后,彻底停止应用并重新构建。Hot Reload 和 Hot Restart 无法注册新加入的原生插件。
- 检查
MainActivity是否使用 Flutter Android V2 embedding。 - 如果重写了
configureFlutterEngine(),确认其中调用了super.configureFlutterEngine(flutterEngine),否则可能跳过自动插件注册。 - 核对 Flutter、Dart 与
phonepe_payment_sdk: ^2.0.1的实际兼容性,并使用与该插件版本对应的 PhonePe 接入文档。
为什么会出现这个异常
Flutter 插件通常由两部分组成:
- Dart 端 API,例如
PhonePePaymentSdk.init(...) - Android 原生实现,通过 MethodChannel 与 Dart 通信
如果 Dart 端可以正常加载,但 Android 原生代码没有编译进应用,或者没有注册到 FlutterEngine,调用时就会抛出类似异常:
MissingPluginException(
No implementation found for method init on channel ...
)
因此,修改 merchantId、saltKey、请求体或 checksum 不能解决这个问题。这些参数只有在插件注册成功并进入原生方法后才会产生影响。
1. 必须进行完整重建
新增原生插件后,只执行 Hot Reload 或 Hot Restart 不够。请先彻底停止正在运行的应用,再执行:
flutter clean
flutter pub get
flutter run
如果设备上还安装着旧版本,也可以先卸载应用,再执行 flutter run。
同时检查依赖是否已经成功解析:
flutter pub deps
输出中应包含 phonepe_payment_sdk。
2. 检查运行平台
PhonePe Payment SDK 的原生支付能力依赖 Android 实现。可以在调用前明确限制平台:
import 'dart:io';
Future<void> initiatePayment() async {
if (!Platform.isAndroid) {
setState(() {
result = 'PhonePe Payment SDK 只能在其支持的 Android 环境中调用';
});
return;
}
try {
final value = await PhonePePaymentSdk.init(
environmentValue,
appId,
merchantId,
enableLogging,
);
if (!mounted) return;
setState(() {
result = 'PhonePe SDK Initialized - $value';
});
} catch (error) {
if (!mounted) return;
setState(() {
result = error.toString();
});
}
}
不要直接在以下环境中验证这个原生方法:
- Flutter Web
- Windows、macOS 或 Linux 桌面应用
- 没有加载 Android 原生插件的单元测试
- 仅通过 Dart VM 运行的测试环境
具体支持哪些平台,应以当前插件版本的说明为准。如果某个平台没有对应的原生实现,在该平台调用就会出现 MissingPluginException。
3. 检查 MainActivity
项目使用 Flutter Android V2 embedding 时,Kotlin 入口通常只需要:
package com.example.your_app
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()
对应的 Java 写法是:
package com.example.your_app;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}
正常情况下,Flutter 会自动注册 pubspec.yaml 中声明的插件,不需要手动调用 PhonePe 的注册方法。
重写 configureFlutterEngine 的情况
如果项目自定义了 configureFlutterEngine(),需要保留父类调用:
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 其他自定义 MethodChannel 配置
}
}
缺少 super.configureFlutterEngine(flutterEngine) 时,自动生成的插件注册逻辑可能不会执行。
还要检查项目是否仍在使用旧入口:
import io.flutter.app.FlutterActivity;
这是旧版 embedding 使用的类。现代 Flutter 项目应改用:
import io.flutter.embedding.android.FlutterActivity;
如果项目经历过 Flutter 版本迁移,可以创建一个同版本的新 Flutter 工程,对照检查 MainActivity、AndroidManifest.xml 和 Gradle 配置。
4. 正确配置 Android 仓库
如果当前 PhonePe SDK 版本明确要求通过指定仓库获取 Android 依赖,可以在项目级 android/build.gradle 中保留以下配置:
allprojects {
repositories {
google()
mavenCentral()
maven {
url "https://phonepe.mycloudrepo.io/public/repositories/phonepe-intentsdk-android"
}
}
}
jcenter() 已停止维护。除非某个旧依赖明确要求使用它,否则应优先选择 mavenCentral()。
仓库缺失一般会导致 Gradle 在编译阶段报依赖解析错误,而不是在运行时抛出 MissingPluginException。如果 APK 可以正常编译,但调用插件时报错,应优先检查运行平台和插件注册情况。
部分较新的 Android 和 Gradle 工程会在 android/settings.gradle 的 dependencyResolutionManagement 中统一配置仓库,例如:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url "https://phonepe.mycloudrepo.io/public/repositories/phonepe-intentsdk-android"
}
}
}
使用哪种方式取决于项目采用的 Gradle 模板。在没有确认模板前,不要同时机械地添加两套配置。如果启用了 FAIL_ON_PROJECT_REPOS,项目级 build.gradle 中的仓库配置可能不会被接受。
5. 调整初始化顺序
下面的调用顺序通常不是 MissingPluginException 的根因,但建议先执行 super.initState(),再初始化 SDK:
@override
void initState() {
super.initState();
body = getCheckSum();
initiatePayment();
}
字段声明也应符合 Dart 的空安全要求:
Object? result;
String body = '';
String checksum = '';
异步逻辑可以改用 async/await,减少嵌套的 then() 和 setState():
Future<void> initiatePayment() async {
try {
final initialized = await PhonePePaymentSdk.init(
environmentValue,
appId,
merchantId,
enableLogging,
);
if (!mounted) return;
setState(() {
result = 'PhonePe SDK Initialized - $initialized';
});
} catch (error, stackTrace) {
debugPrint('PhonePe initialization failed: $error');
debugPrintStack(stackTrace: stackTrace);
if (!mounted) return;
setState(() {
result = error.toString();
});
}
}
6. 检查版本兼容性
当前配置是:
environment:
sdk: ">=2.2.0 <3.0.0"
dependencies:
crypto: ^3.0.2
phonepe_payment_sdk: ^2.0.1
Flutter 3.0.0 使用 Dart 2.17,因此这个 SDK 范围在语法上允许 Dart 2.17。不过,这只能说明 Dart 版本约束没有直接冲突,不能证明 phonepe_payment_sdk 2.0.1 与项目使用的 Android Gradle Plugin、Kotlin、compile SDK 和 PhonePe 原生依赖完全兼容。
建议执行:
flutter doctor -v
flutter pub outdated
flutter pub get
如果 Gradle 构建日志中出现 Kotlin、Android SDK 或依赖解析错误,应先根据当前插件版本的要求解决这些问题。不要直接套用其他 PhonePe SDK 大版本的 Gradle 参数或 API,因为不同版本的初始化参数和支付流程可能已经发生变化。
7. 如果应用使用了自定义 FlutterEngine
如果应用缓存、复用或自行创建了 FlutterEngine,插件必须注册到实际承载当前 Flutter 页面运行的 engine。即使插件已经注册到另一个 engine,当前页面调用时仍可能抛出 MissingPluginException。
自定义 engine 时,需要确保执行生成的插件注册逻辑:
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
val flutterEngine = FlutterEngine(context)
GeneratedPluginRegistrant.registerWith(flutterEngine)
普通 Flutter 应用不需要手动执行这段代码。只有确定项目自行管理 FlutterEngine 时才需要检查,以免重复注册插件。
安全注意事项
不要把示例中的 saltKey 保存在 Flutter 客户端:
String saltKey = "...";
Flutter 应用可以被反编译,写入客户端的密钥不能被视为秘密。在生产环境中,请求签名、checksum 或授权信息应由可信后端生成,客户端只接收发起支付所需的临时数据。
同样,不建议在 initState() 中调用 getCheckSum(),由客户端生成生产支付签名。即使这种方式可以用于测试,也不适合作为正式的支付架构。
推荐排查顺序
先确认应用运行在 Android 上,再彻底停止应用,依次执行 flutter clean、flutter pub get 和 flutter run。如果问题仍然存在,检查 MainActivity 是否使用 V2 embedding,以及自定义的 configureFlutterEngine() 是否调用了父类实现。
这些配置都正常时,再检查项目是否使用了自定义 FlutterEngine,并核对 phonepe_payment_sdk 2.0.1 支持的平台及其 Android 构建要求。插件成功注册后,才需要继续排查 merchant 配置、请求体、callback 和 checksum。
备注:内容仅供参考。
