Flutter集成HALO SDK插件halo_sdk_flutter_plugin的使用

Flutter集成HALO SDK插件halo_sdk_flutter_plugin的使用

Halo SDK Flutter Plugin

一个基于Flutter实现的Halo Dot SDK。

Halo Dot SDK 是一种隔离式MPoC支付处理软件,具有认证和监控能力。

下图描述了这种隔离式MPoC支付软件的架构。

下图还展示了SDK边界及其与集成渠道和第三方支付网关之间的交互。它还描述了SDK边界以及它如何与集成商和第三方支付进行交互,并包含有关数据在边界之间通信的详细信息。

Halo Dot SDK Architecture


目录


需求

以下是实现Halo Dot SDK所需的需求列表:

  • 开发者账户(在开发者门户注册)
  • 签署的保密协议/NDA(可在开发者门户找到)
  • 生成JWT所需的公钥和私钥(公钥提交到开发者门户)
  • Kotlin 2.0.21(@注释:更高版本正在开发中)
  • Flutter 3.27.3(@注释:更高版本正在开发中)
  • Dart 3.6.1(@注释:随Flutter捆绑)
  • Java 21
  • IDE(推荐使用Android Studio
  • 推荐库:
  • 常见问题解答

开发者门户注册

在测试生产环境之前,您需要在我们的QA(UAT - 用户验收测试环境)上注册。

开发者门户允许您:

  1. 接受保密协议
  2. 访问SDK
  3. 提交您的公钥(用于验证JWT)
  4. 获取JWT的相关细节

注册

  1. 访问开发者门户并注册。
  2. 通过OTP验证您的账户。
  3. 点击访问SDK按钮:
  4. 下载并接受保密协议。
  5. 提交您的公钥并创建一个发行人名称。这将用于验证您创建的JWT:
  6. 将生成访问密钥和秘密密钥。这些将在您的IDE中用于访问Halo SDK:

开始使用

确保您已设置好构建Flutter应用的环境。您可以按照这里的说明进行操作。

Flutter应用

创建一个新的Flutter应用或将其集成到现有的应用程序中。确保添加了Android支持,这是目前唯一支持的平台。

# 使用flutter
flutter create . --project-name my_sdk_flutter_plugin --org za.co.synthesis.halo.test.plugin
# 使用fvm
fvm spawn 3.27.3 create . --project-name my_sdk_flutter_plugin --org za.co.synthesis.halo.test.plugin

环境

  1. SDK使用Java 21进行了测试。我们不能确认更高的版本是否能正常工作。
  2. SDK使用Flutter 3.27.3 和 Dart 3.6.1 进行了测试(DevTools 2.40.2)。
  3. Android项目的minSdkVersion应为29或更高。请在android/app/build.gradle文件中检查此值。
defaultConfig {  
  applicationId "za.co.synthesis.halo.sdkflutterplugin_example" 
  minSdkVersion 29 // 应该为29或更高 
  // ...
}  
  1. 如果遇到设置minSdkVersion的问题,请查看常见问题解答。

插件安装

  1. 运行flutter pub add halo_sdk_flutter_plugin以将Flutter插件添加到您的项目中。
  2. 我们建议同时安装flutter pub add permission_handler
  3. 插件需要从Halo Maven存储库(托管在AWS S3上)下载SDK二进制文件。为此,您需要凭据。在开发者门户中找到您的accesskeysecretkey

    将其添加到您的android/local.properties文件中(如果不存在则创建一个):
aws.accesskey=<accesskey>  
aws.secretkey=<secretkey>  

注意大小写

  1. 然后将以下内容添加到您的android/app/build.gradle文件中(可能已经存在):
def localProperties = new Properties()  
def localPropertiesFile = rootProject.file('local.properties')  
if (localPropertiesFile.exists()) {  
 localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) }}  

移动后端需求

JWT

所有调用Halo SDK的操作都需要有效的JWT。
有关创建JWT的详细信息可以在开发者门户中找到。
我们建议安装flutter pub add dart_jsonwebtoken来生成JWT。

config.dart
class Config {  
  static const String privateKeyPem = String.fromEnvironment('PRIVATE_KEY', defaultValue: '');  
  static const String issuer = "{get from the [Developer portal]}";  
  static const String username = "{get from the [Developer portal]}";  
  static const String merchantId = "{get from the [Developer portal]}";  
  static const String host = "{get from the [Developer portal]}";  
  static const String aud = "{get from the [Developer portal]}"; 
  static const String ksk = "{get from the [Developer portal]}";     
}
jwt_token.dart
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';  
import './config.dart';  // 引入上述配置文件
  
class JwtToken {  
  static String getJwt() {  
    final jwt = JWT(  
      {'aud_fingerprints': Config.aud, 'ksk_pin': Config.ksk, 'usr': Config.username},  
      audience: Audience([Config.host]),  
      issuer: Config.issuer,  
      subject: Config.merchantId,  
    );  
  
    final key = RSAPrivateKey(Config.privateKeyPem);  
    final token = jwt.sign(key, algorithm: JWTAlgorithm.RS512);  
    return token;  
  }  
}

私钥不应存储在代码仓库中。

当Halo Dot SDK调用onRequestJWT时,您应该将生成的JWT传递给回调函数。

JWT生命周期

由于JWT本质上授权特定商户用户进行支付接受,因此应尽可能缩短JWT的有效期,以限制攻击者破解密钥的时间,并在密钥泄露的情况下限制损害范围。

推荐的生命周期为15分钟。

JWT签名公钥格式

JWT公钥应作为证书发布,以文本友好的格式,例如B64编码的PEM(.crt、.pem)。

JWT声明

JWT必须包含以下声明——除了aud_fingerprints(受众指纹)之外,其他均为标准声明:

字段 类型 备注
alg String 签名算法为RSA签名SHA-256哈希,别名为RS256。需要非对称加密方案以允许Kernel Server验证令牌而不生成它。
sub String 支付处理器商户用户ID或应用ID
iss String JWT发行者的唯一标识符(从Halo服务器的角度看),由JWT发行者和Synthesis预先协商并在Halo服务器中配置。
aud String Halo服务器TLS端点的URL,例如kernelserver.qa.haloplus.io。此值应从Synthesis获取(每个环境不同)。
usr String 执行交易的用户详细信息,通常是登录到集成商应用程序的用户名。
iat NumericDate JWT生成的UTC时间戳。
exp NumericDate JWT的过期UTC时间。
aud_fingerprints String Kernel Server TLS端点预期的SHA-256指纹的CSV列表。此列表可能包含多个值以支持证书轮换。

所有这些值都可以通过向https://kernelserver.qa.haloplus.io/<sdk-version>/tokens/checkjwt发起请求进行验证。
方法:POST
头部:Bearer Auth


使用

  1. 首先,您需要请求SDK所需的权限。在您的AndroidManifest.xml文件中添加以下权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="za.co.synthesis.halo.sdkflutterplugin_example">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.NFC"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.VIBRATE"/>

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />

    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    <!--  ....  -->
</manifest xmlns:android="http://schemas.android.com/apk/res/android">
  1. 在Flutter应用中添加插件以帮助请求权限。在您的pubspec.yaml文件中添加以下内容:
dependencies:  
  permission_handler: ^11.3.1 # ...

确保在android/app/build.gradle中,compileSdkVersiontargetSdkVersion设置为34或更高。

记住在更改pubspec.yaml文件后运行flutter pub get

  1. 在初始化SDK之前,在Flutter应用中请求权限。这是一个示例(请处理用户拒绝权限的情况):
    我们建议创建一个名为permission.dart的文件来请求所有需要的权限,如下所示:
Future<void> checkPermissions() async {
  var permissions = [
    Permission.camera,
    Permission.bluetoothConnect,
    Permission.bluetoothScan,
    Permission.location,
  ];

  for (var permission in permissions) {
    await requestPermission(permission);
  }
}

Future<void> on(Permission permission) async {
  var permissionStatus = await permission.status;
  if (permissionStatus.isGranted) {
    debugPrint("${permission} permission is granted, not requesting");
  } else if (permissionStatus.isPermanentlyDenied) {
    debugPrint("${permission} permission is permanently denied, enable in settings");
  } else {
    var requestResult = await permission.request();

    if (requestResult.isGranted) {
      debugPrint("${permission} permission is granted");
    } else if (requestResult.isPermanentlyDenied) {
      debugPrint("${permission} permission is permanently denied, enable in settings");
    }
  }
}
  1. 您的Android MainActivity(通常位于app/src/main/kotlin/

例如:

import za.co.synthesis.halo.sdkflutterplugin.HaloActivity  
  
class MainActivity: HaloActivity() {  
}
  1. 在您的Flutter项目中,现在可以使用插件与SDK交互。这是一个示例:
  • 首先,您需要在Flutter项目中实现IHaloCallbacks接口,以便接收来自SDK的回调。这是一个示例:
// 调用此文件为 halo_sdk.dart
import './jwt_token';

class HaloCallbacks implements IHaloCallbacks {
  @override
  void onAttestationError(HaloAttestationHealthResult details) {
    debugPrint("example app: attestation error: $details");
  }

  @override
  void onHaloTransactionResult(HaloTransactionResult result) {
    debugPrint("example app: transaction result: $result");
  }

  @override
  void onHaloUIMessage(HaloUIMessage message) {
    debugPrint("example app: UI message: $message");
  }

  @override
  void onInitializationResult(HaloInitializationResult result) {
    debugPrint("example app: initialization message: $result");
  }

  @override
  void onRequestJWT(void Function(String jwt) callback) {
    debugPrint("example app: onRequestJWT");
    // JwtToken 导入自 jwt_token.dart
    var jwt = JwtToken.getJwt(); // 您需要传递创建的JWT。
    callback(jwt);
  }

  @override
  void onSecurityError(errorCode) {
    debugPrint("example app: security error: $errorCode");
  }

  @override
  void onCameraControlLost() {
    debugPrint("example app: camera control lost");
  }
}

您可以根据SDK通过回调发送的内容决定如何影响UI。

  • 接下来,您希望在处理交易的Widget打开时初始化SDK。这是一个示例:
void onInitializeSdk(BuildContext context) {  
  var haloCallbacks = HaloCallbacks(); // 定义在之前的代码中 
  String package = "za.co.synthesis.halo.sdkflutterplugin_example"; 
  String appVersion = "0.0.2";
  int onStartTransactionTimeOut = 300000;
  try {  
    Sdkflutterplugin.initializeHaloSDK(haloCallbacks, package, appVersion, onStartTransactionTimeOut);  
  } on PlatformException catch (e) {  
    String message = "SDK initialisation error: ${e.code} ${e.message}";  
    setUiMessage(UiMessage(message, Colors.red));  
  }  
}  
  • 接下来,您可以启动交易。这是一个示例:
Sdkflutterplugin.startTransaction(1.00, 'Some merchant reference', 'ZAR');  

从这一点开始,一系列UI消息将被推送到注册的回调中。
您将使用这些消息向用户提供适当的UI/文本。


文档


测试

所有交易在签署保密协议(NDA)之前均无效。
您可以使用虚拟卡(如Vida Mobile CDET)进行测试。


常见问题解答

问题:如何设置我的compileSdkVersion,如果当前设置为flutter.compileSdkVersion

答案:
您可以在local.properties文件中设置compileSdkVersion

sdk.dir=/home/{me}/android-sdk/  
flutter.sdk=/home/{me}/fvm/versions/3.27.3  
flutter.buildMode=debug  
flutter.versionName=1.0.0  
flutter.versionCode=1  
flutter.compileSdkVersion=34  
flutter.minSdkVersion=29

然后在android/app/build.gradle文件中引用此值:

  compileSdkVersion localProperties.getProperty('flutter.compileSdkVersion').toInteger()

更多关于Flutter集成HALO SDK插件halo_sdk_flutter_plugin的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter集成HALO SDK插件halo_sdk_flutter_plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中集成HALO SDK插件 halo_sdk_flutter_plugin 可以帮助你快速接入HALO平台的功能。下面是一个简单的步骤指南,帮助你开始使用 halo_sdk_flutter_plugin

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 halo_sdk_flutter_plugin 的依赖。

dependencies:
  flutter:
    sdk: flutter
  halo_sdk_flutter_plugin: ^<version>  # 请替换为最新版本

然后,运行 flutter pub get 来获取依赖。

2. 初始化HALO SDK

在你的Flutter应用的 main.dart 文件中,初始化HALO SDK。通常,你可以在 main 函数中进行初始化。

import 'package:flutter/material.dart';
import 'package:halo_sdk_flutter_plugin/halo_sdk_flutter_plugin.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化HALO SDK
  await HaloSdkFlutterPlugin.init(
    appId: 'your_app_id', // 替换为你的App ID
    appKey: 'your_app_key', // 替换为你的App Key
    // 其他可选配置
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

3. 使用HALO SDK的功能

根据你的需求,你可以使用 halo_sdk_flutter_plugin 提供的各种功能。以下是一些常见的用法示例:

3.1 用户登录

void loginUser() async {
  try {
    await HaloSdkFlutterPlugin.login(username: 'user', password: 'password');
    print('Login successful');
  } catch (e) {
    print('Login failed: $e');
  }
}

3.2 获取用户信息

void getUserInfo() async {
  try {
    var userInfo = await HaloSdkFlutterPlugin.getUserInfo();
    print('User Info: $userInfo');
  } catch (e) {
    print('Failed to get user info: $e');
  }
}

3.3 发送事件

void sendEvent() async {
  try {
    await HaloSdkFlutterPlugin.trackEvent(eventName: 'button_click', properties: {'button_id': 'login_button'});
    print('Event sent');
  } catch (e) {
    print('Failed to send event: $e');
  }
}

4. 处理回调

你可以通过设置回调来处理一些事件,例如用户登录状态的变化。

void setCallbacks() {
  HaloSdkFlutterPlugin.setOnLoginStatusChangedListener((isLoggedIn) {
    print('Login status changed: $isLoggedIn');
  });
}

5. 处理权限和配置

根据你的应用需求,你可能需要在 AndroidManifest.xmlInfo.plist 中添加一些权限和配置。

Android

android/app/src/main/AndroidManifest.xml 中添加必要的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

iOS

ios/Runner/Info.plist 中添加必要的权限:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

6. 运行应用

完成上述步骤后,你可以运行你的Flutter应用,并测试HALO SDK的功能。

flutter run

7. 调试和日志

你可以启用调试日志来帮助调试。

void enableDebugLogging() {
  HaloSdkFlutterPlugin.setDebugEnabled(true);
}

8. 处理错误

在使用过程中,可能会遇到一些错误。你可以通过捕获异常来处理这些错误。

void handleErrors() async {
  try {
    // 调用HALO SDK的方法
  } catch (e) {
    print('An error occurred: $e');
  }
}
回到顶部