PAYATHON 2026

如何在 codename one 中集成 Interswitch Payment SDK for Android

支付阿杰

结论

Interswitch Payment SDK 是原生 Android SDK,无法直接在 Codename One 的跨平台 Java 代码中调用。接入时需要:

  1. 将 SDK 的 Maven/AAR 依赖加入 Codename One 的 Android 构建。
  2. 创建 Codename One NativeInterface。
  3. 在 native/android 中实现 Android 桥接代码。
  4. 将支付成功、取消和失败结果转换为 Codename One 能处理的数据。
  5. 为 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. 支付结果必须由服务器验证

客户端收到”成功”回调后,不能直接把订单标记为已付款。可靠的处理流程如下:

  1. 服务器生成唯一交易号。
  2. Codename One 客户端使用该交易号发起支付。
  3. 客户端收到 SDK 回调后,将交易号发送给服务器。
  4. 服务器通过 Interswitch 的服务端接口或 Webhook 核验交易。
  5. 服务器确认金额、币种、商户和交易状态全部一致后,再更新订单。

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。

备注:内容仅供参考。