如何在 codename one 中集成 Interswitch Payment SDK for Android
结论
Interswitch Payment SDK 是原生 Android SDK,无法直接在 Codename One 的跨平台 Java 代码中调用。接入时需要:
- 将 SDK 的 Maven/AAR 依赖加入 Codename One 的 Android 构建。
- 创建 Codename One
NativeInterface。 - 在
native/android中实现 Android 桥接代码。 - 将支付成功、取消和失败结果转换为 Codename One 能处理的数据。
- 为 iOS 和其他平台提供空实现,或在不支持的平台隐藏支付入口。
Android Studio 教程中的 Activity、Context、Intent 和 Manifest 配置不能直接复制到 Codename One 的公共源码目录,必须放入 Android 原生实现。
1. 先确认 SDK 的交付方式
Interswitch 的依赖名称、初始化参数和支付 API 可能随 SDK 版本变化。请以当前版本的官方集成指南为准,并确认以下内容:
- SDK 是通过 Maven/Gradle 引入,还是以
.aar/.jar文件交付 - Maven 的
groupId、artifactId和版本号 - 是否需要配置仓库地址
- 是否需要修改
AndroidManifest.xml - 是否需要 API Key、Client ID、Merchant ID 等参数
- 支付结果是通过回调、
Activity Result,还是 SDK 自带的监听器返回 - 是否需要 ProGuard/R8 规则
- 最低 Android API Level 要求
下面的代码只展示 Codename One 侧的桥接结构。Interswitch 的类名和方法名必须替换为所用 SDK 版本的真实 API,不能把示例中的占位名称当作官方接口。
2. 添加 Android SDK 依赖
如果 Interswitch SDK 已发布到 Maven 仓库,优先使用 Gradle 依赖。可以在 Codename One 项目的构建提示中添加:
codename1.arg.android.gradleDep=groupId\:artifactId\:version
其中:
groupId:artifactId:version
需要替换为 Interswitch 文档提供的实际坐标。属性中的冒号是否需要转义,要看 Codename One 的设置界面和项目格式。添加后应检查生成的 codenameone_settings.properties。
如果依赖位于 Maven Central 之外,还要按照当前 Codename One 构建系统支持的方式配置仓库。不要随意拼接 Gradle 脚本,因为不同版本的 Codename One 对自定义 Gradle 配置的支持可能不同。
如果官方只提供 .aar,可以为 SDK 创建一个 Codename One Library(CN1Lib),把 .aar 和 Android 原生桥接实现封装在库中。同时要检查该 AAR 是否依赖其他库。只复制一个 .aar 文件,通常不会自动包含它的传递依赖。
3. 定义跨平台接口
在 Codename One 公共源码中定义接口,并且只使用跨平台类型:
package com.example.payment;
import com.codename1.system.NativeInterface;
public interface InterswitchNative extends NativeInterface {
void initialize(
String clientId,
String merchantId,
String environment
);
void startPayment(
String transactionReference,
String amount,
String currency,
String customerEmail
);
}
原生接口的方法参数尽量限制为:
String- 基本数值类型
boolean- 简单数组
不要在接口中暴露 Android 的 Activity、Context、Intent,也不要使用 Interswitch SDK 自己的对象。公共代码和非 Android 构建无法识别这些类型。
获取接口实例:
package com.example.payment;
import com.codename1.system.NativeLookup;
public final class InterswitchGateway {
private static final InterswitchNative NATIVE =
NativeLookup.create(InterswitchNative.class);
private InterswitchGateway() {
}
public static boolean isAvailable() {
return NATIVE != null && NATIVE.isSupported();
}
public static void initialize(
String clientId,
String merchantId,
String environment
) {
if (!isAvailable()) {
throw new IllegalStateException(
"Interswitch payment is not supported on this platform"
);
}
NATIVE.initialize(clientId, merchantId, environment);
}
public static void pay(
String reference,
String amount,
String currency,
String email
) {
if (!isAvailable()) {
throw new IllegalStateException(
"Interswitch payment is not supported on this platform"
);
}
NATIVE.startPayment(reference, amount, currency, email);
}
}
NativeInterface 要求实现 isSupported()。Android 实现返回 true,其他平台可以返回 false。
4. 编写 Android 原生实现
在 Codename One 项目的 Android 原生源码目录中,创建与接口对应的实现类。包名必须一致,类名通常是在接口名后加上 Impl:
package com.example.payment;
import android.app.Activity;
import com.codename1.impl.android.AndroidNativeUtil;
public class InterswitchNativeImpl {
private String clientId;
private String merchantId;
private String environment;
public boolean isSupported() {
return true;
}
public void initialize(
String clientId,
String merchantId,
String environment
) {
this.clientId = clientId;
this.merchantId = merchantId;
this.environment = environment;
Activity activity = AndroidNativeUtil.getActivity();
/*
* 在这里调用当前 Interswitch SDK 的初始化 API。
*
* 例如可能需要:
* - Activity 或 application Context
* - Client ID
* - Merchant ID
* - 测试或生产环境
*
* 请使用 SDK 文档中的真实类名和方法,不要照搬占位代码。
*/
}
public void startPayment(
String transactionReference,
String amount,
String currency,
String customerEmail
) {
Activity activity = AndroidNativeUtil.getActivity();
if (activity == null) {
PaymentEvents.fireFailure(
transactionReference,
"Android Activity is not available"
);
return;
}
/*
* 在这里按照 Interswitch Android 指南:
*
* 1. 创建支付请求对象;
* 2. 设置 reference、amount、currency、email;
* 3. 启动 SDK 的支付 Activity 或支付方法;
* 4. 在 SDK 回调中调用 PaymentEvents。
*/
}
}
这里没有填写具体的 Interswitch 类名,因为题目未提供 SDK 版本和完整 API。不同产品或版本可能采用不同的初始化器、支付请求模型和结果回调。凭空补充类名虽然会让示例看起来更完整,但代码很可能无法编译。
5. 把异步支付结果送回 Codename One
支付通常是异步操作。可以创建一个公共事件入口,由 Android 回调将结果转发到 Codename One EDT:
package com.example.payment;
import com.codename1.ui.Display;
public final class PaymentEvents {
public interface Listener {
void onSuccess(String reference, String responseData);
void onCancelled(String reference);
void onFailure(String reference, String message);
}
private static Listener listener;
private PaymentEvents() {
}
public static void setListener(Listener value) {
listener = value;
}
public static void fireSuccess(
final String reference,
final String responseData
) {
Display.getInstance().callSerially(() -> {
Listener current = listener;
if (current != null) {
current.onSuccess(reference, responseData);
}
});
}
public static void fireCancelled(final String reference) {
Display.getInstance().callSerially(() -> {
Listener current = listener;
if (current != null) {
current.onCancelled(reference);
}
});
}
public static void fireFailure(
final String reference,
final String message
) {
Display.getInstance().callSerially(() -> {
Listener current = listener;
if (current != null) {
current.onFailure(reference, message);
}
});
}
}
在应用中注册监听器:
PaymentEvents.setListener(new PaymentEvents.Listener() {
@Override
public void onSuccess(String reference, String responseData) {
Dialog.show(
"支付已提交",
"交易号:" + reference,
"确定",
null
);
// 继续向自己的服务器查询并验证交易状态
}
@Override
public void onCancelled(String reference) {
Dialog.show("已取消", "用户取消了支付", "确定", null);
}
@Override
public void onFailure(String reference, String message) {
Dialog.show("支付失败", message, "确定", null);
}
});
原生 SDK 的回调可能在 Android 工作线程上执行,而 Codename One UI 必须在 EDT 上更新,所以这里需要使用 Display.callSerially()。
如果 SDK 使用 startActivityForResult(),应优先采用当前 Codename One 版本提供的 Android Activity-result 桥接机制,不要自行覆盖主 Activity 的 onActivityResult()。覆盖不当可能影响 Codename One 的相机、文件选择或其他原生功能。
6. 调用支付功能
应用启动时初始化一次:
if (InterswitchGateway.isAvailable()) {
InterswitchGateway.initialize(
clientId,
merchantId,
"SANDBOX"
);
}
用户确认订单后发起支付:
String reference = createReferenceOnServer();
String amount = "1500.00";
String currency = "NGN";
String email = "customer@example.com";
InterswitchGateway.pay(
reference,
amount,
currency,
email
);
交易号最好由服务器生成,并确保唯一。金额的格式和单位必须遵循当前 Interswitch SDK 的规定。有些支付接口接收 "1500.00",另一些则接收最小货币单位整数,例如 150000。未核对文档前,不要自行判断格式。
7. Manifest、权限和混淆配置
如果 Android Studio 指南要求添加权限、Activity、Service、Provider 或 <meta-data>,这些配置也要写入最终生成的 Android Manifest。
常见配置可能包括网络权限:
<uses-permission android:name="android.permission.INTERNET" />
不要因为”支付 SDK 通常需要”就自行添加其他敏感权限。只加入当前 Interswitch 文档明确要求的配置。
如果文档提供了 R8/ProGuard 规则,应通过 Codename One 支持的 Android 构建提示或原生库配置完成合并。不要通过关闭全部混淆来掩盖问题。
生成构建后,应检查最终 Android 工程或构建日志,确认:
- Interswitch 依赖已经下载并打包
- Manifest 配置已成功合并
minSdkVersion满足要求- 不存在重复依赖或 AndroidX 冲突
- SDK 所需资源已经包含
- Release 构建没有因混淆删除回调类
8. 支付结果必须由服务器验证
客户端收到”成功”回调后,不能直接把订单标记为已付款。可靠的处理流程如下:
- 服务器生成唯一交易号。
- Codename One 客户端使用该交易号发起支付。
- 客户端收到 SDK 回调后,将交易号发送给服务器。
- 服务器通过 Interswitch 的服务端接口或 Webhook 核验交易。
- 服务器确认金额、币种、商户和交易状态全部一致后,再更新订单。
API Secret、私钥以及其他用于服务端鉴权的凭据不能写入 Codename One 应用。APK 可以被反编译,因此保存在客户端中的秘密不能视为安全。
9. 建议封装成 CN1Lib
如果项目需要长期维护,可以将以下内容封装为独立 CN1Lib:
InterswitchNative- Android 原生实现
- Maven/AAR 依赖
- Manifest 配置
- 混淆规则
- 支付结果模型
- SDK 版本说明
封装后,应用层只需调用统一的 initialize() 和 pay()。以后升级 Interswitch SDK 时,业务代码也不必混入大量 Android 细节。
实际接入前,先确认所用 Interswitch SDK 的准确版本、Gradle 依赖和支付调用代码,再将 Android Studio 指南中涉及 Android API 的部分逐项迁移到 native/android 实现。Codename One 公共代码只负责传递参数、维护界面状态和配合服务端验证,不直接依赖 Android SDK。
备注:内容仅供参考。