HarmonyOS鸿蒙Next中flutter与原生通信出现了问题

HarmonyOS鸿蒙Next中flutter与原生通信出现了问题

原生端

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
 */

import { util } from '@kit.ArkTS';
import { Logger } from './Logger';

// 日志标签
const TAG: string = 'JWSUtil';
// Base64编码填充计算常量
const BASE64_PADDING_MOD: number = 4;
// 无效的Base64填充长度
const BASE64_PADDING_INVALID: number = 1;

/**
 * JWS工具类,用于解码JWS格式的订单信息
 */
export class JWSUtil {
  /**
   * 解码JWS格式的字符串
   * @param data JWS格式的字符串
   * @returns 解码后的订单信息字符串
   */
  public static decodeJwsObj(data: string): string {
    // 将JWS字符串按"."分割成三部分(头部、载荷、签名)
    const jws: string[] = data.split('.');
    let result: string = '';
    // 校验JWS格式是否正确(至少包含三部分)
    if (jws.length < 3) {
      return result;
    }
    try {
      // 创建UTF-8解码器(忽略BOM头)
      const textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true });
      // 创建Base64工具实例
      const base64 = new util.Base64Helper();
      // 获取载荷部分(JWS的第二部分)
      let payload = jws[1];
      // 计算需要补充的填充字符
      const pad = payload.length % BASE64_PADDING_MOD;
      if (pad) {
        // 无效的填充长度(Base64编码不允许余1的情况)
        if (pad === BASE64_PADDING_INVALID) {
          throw new Error('InvalidLengthError: 输入的Base64字符串长度无法正确填充');
        }
        // 补充"="字符使长度满足Base64要求
        payload += new Array(BASE64_PADDING_MOD - pad + 1).join('=');
      }
      // 先Base64解码,再UTF-8解码得到原始字符串
      result = textDecoder.decodeToString(base64.decodeSync(payload));
    } catch (err) {
      Logger.error(TAG, `解码JWS失败: ${JSON.stringify(err)}`);
    }
    return result;
  }
}
import { iap } from '@kit.IAPKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { MethodChannel, MethodResult, MethodCall } from '@ohos/flutter_ohos';
import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos';
import { JWSUtil } from '../common/JWSUtil';

// 类型定义
interface PurchaseData {
  type: number;
  jwsPurchaseOrder?: string;
}

interface PurchaseOrderPayload {
  purchaseOrderId: string;
  purchaseToken: string;
  productType: string | number;
  productId: string;
  purchaseTime: number;
  finishStatus?: string;
}

interface ProductInfo {
  productId: string;
  productName: string;
  price: string;
  description: string;
  productType: number;
}

interface PurchasedProductInfo {
  orderId: string;
  productId: string;
  productType: number;
  purchaseTime: string;
  status: string;
}

interface PurchaseParams {
  productId: string;
  productType: number;
}

interface EventData {
  success?: boolean;
  message?: string;
  products?: ProductInfo[];
  purchases?: PurchasedProductInfo[];
  orderId?: string;
  productId?: string;
}

export default class HuaweiIapPlugin implements FlutterPlugin {
  private methodChannel?: MethodChannel;
  private context: common.UIAbilityContext = {} as common.UIAbilityContext;
  private api: IapApi;

  constructor() {
    this.api = new IapApi(this);
  }

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    // 获取应用上下文(关键修复)
    this.context = binding.getApplicationContext() as common.UIAbilityContext;

    this.methodChannel = new MethodChannel(
      binding.getBinaryMessenger(),
      'com.nobook.payment/huawei_iap'
    );

    // 使用that保存当前实例引用
    let that = this;
    this.methodChannel.setMethodCallHandler({
      onMethodCall: (call: MethodCall, result: MethodResult) => {
        // 检查上下文有效性
        if (!that.context) {
          result.error('100', '上下文无效,IAP初始化失败', null);
          return;
        }

        switch (call.method) {
          case 'init':
            that.api.initializeIap(result);
            break;
          case 'queryProducts':
            that.api.queryProducts(result);
            break;
          case 'purchaseProduct':
            that.api.purchaseProduct(call.args, result);
            break;
          case 'queryPurchases':
            that.api.queryPurchases(result);
            break;
          default:
            result.notImplemented();
            break;
        }
      }
    });
  }

  // 提供给IapApi访问上下文的方法
  getContext(): common.UIAbilityContext {
    return this.context;
  }

  // 提供给IapApi发送事件的方法
  sendEvent(eventName: string, data: EventData): void {
    this.methodChannel?.invokeMethod(eventName, data);
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    this.methodChannel?.setMethodCallHandler(null);
  }

  getUniqueClassName(): string {
    return "HuaweiIapPlugin";
  }
}

// 核心业务逻辑类(分离实现,类似ScreenSaveApi)
class IapApi {
  private plugin: HuaweiIapPlugin;

  constructor(plugin: HuaweiIapPlugin) {
    this.plugin = plugin;
  }

  // 初始化IAP环境
  async initializeIap(result: MethodResult): Promise<void> {
    try {
      const context = this.plugin.getContext();

      // 1. 检查IAP环境
      await iap.queryEnvironmentStatus(context);
      this.plugin.sendEvent('onEnvironmentChecked', { success: true });

      // 2. 查询并处理未完成订单
      await this.queryUnfinishedPurchases();
      this.plugin.sendEvent('onUnfinishedOrdersQueried', { success: true });

      result.success(null);
    } catch (err) {
      const e = err as BusinessError;
      this.plugin.sendEvent('onEnvironmentChecked', {
        success: false,
        message: `环境检查失败: ${e.code}, ${e.message}`
      });
      result.error(e.code.toString(), e.message, null);
    }
  }

  // 查询并处理未完成订单
  private async queryUnfinishedPurchases(): Promise<void> {
    const context = this.plugin.getContext();
    const productTypes = [
      iap.ProductType.CONSUMABLE,
      iap.ProductType.NONCONSUMABLE,
      iap.ProductType.AUTORENEWABLE,
      iap.ProductType.NONRENEWABLE
    ];

    for (const type of productTypes) {
      try {
        const param: iap.QueryPurchasesParameter = {
          productType: type,
          queryType: iap.PurchaseQueryType.UNFINISHED
        };

        const res = await iap.queryPurchases(context, param);
        const purchaseDataList: string[] = res.purchaseDataList || [];

        if (purchaseDataList.length > 0) {
          purchaseDataList.forEach(data => this.processPurchaseData(data));
        }
      } catch (err) {
        console.error(`查询未完成订单失败: ${JSON.stringify(err)}`);
      }
    }
  }

  // 处理购买数据
  private processPurchaseData(purchaseData: string): void {
    try {
      const parsedData = JSON.parse(purchaseData) as PurchaseData;
      const jwsOrder = parsedData.jwsPurchaseOrder;

      if (!jwsOrder) {
        console.error('JWS订单信息无效');
        return;
      }

      const orderStr = JWSUtil.decodeJwsObj(jwsOrder);
      if (!orderStr) {
        console.error('JWS解码失败');
        return;
      }

      const orderPayload = JSON.parse(orderStr) as PurchaseOrderPayload;
      console.warn(`处理未完成订单: ${orderPayload.purchaseOrderId}`);

      if (orderPayload.finishStatus !== '1') {
        this.deliverOrder(orderPayload);
      }
    } catch (e) {
      console.error(`处理购买数据异常: ${JSON.stringify(e)}`);
    }
  }

  // 确认发货
  private async deliverOrder(order: PurchaseOrderPayload): Promise<void> {
    try {
      const context = this.plugin.getContext();
      const param: iap.FinishPurchaseParameter = {
        productType: Number(order.productType),
        purchaseToken: order.purchaseToken,
        purchaseOrderId: order.purchaseOrderId
      };

      await iap.finishPurchase(context, param);
      this.plugin.sendEvent('onDeliveryCompleted', {
        orderId: order.purchaseOrderId,
        success: true
      });
    } catch (err) {
      const e = err as BusinessError;
      this.plugin.sendEvent('onDeliveryCompleted', {
        orderId: order.purchaseOrderId,
        success: false,
        message: `发货失败: ${e.code}, ${e.message}`
      });
    }
  }

  // 查询商品
  async queryProducts(result: MethodResult): Promise<void> {
    try {
      const context = this.plugin.getContext();
      const productIds = ['prod001', 'prod002', 'prod003'];
      const productTypes = [
        iap.ProductType.CONSUMABLE,
        iap.ProductType.NONCONSUMABLE,
        iap.ProductType.AUTORENEWABLE,
        iap.ProductType.NONRENEWABLE
      ];

      const products: ProductInfo[] = [];

      for (const type of productTypes) {
        try {
          const param: iap.QueryProductsParameter = {
            productType: type,
            productIds: productIds
          };

          const res = await iap.queryProducts(context, param);
          products.push(...res.map((p: iap.Product): ProductInfo => ({
            productId: p.id,
            productName: p.name,
            price: p.price,
            description: p.description || '无描述',
            productType: p.type
          })));
        } catch (err) {
          console.error(`查询商品失败: ${JSON.stringify(err)}`);
        }
      }

      this.plugin.sendEvent('onProductsQueried', { products, success: true });
      result.success(null);
    } catch (err) {
      const e = err as BusinessError;
      result.error(e.code.toString(), e.message, null);
    }
  }

  // 购买商品
  async purchaseProduct(params: PurchaseParams, result: MethodResult): Promise<void> {
    try {
      const context = this.plugin.getContext();
      const productId = params.productId;
      const productType = params.productType;

      const param: iap.PurchaseParameter = {
        productId: productId,
        productType: productType,
        quantity: 1
      };

      const purchaseResult = await iap.createPurchase(context, param);

      if (purchaseResult.purchaseData) {
        this.processPurchaseData(purchaseResult.purchaseData);
        this.plugin.sendEvent('onPurchaseResult', {
          success: true,
          productId: productId
        });
      }

      result.success(null);
    } catch (err) {
      const e = err as BusinessError;
      this.plugin.sendEvent('onPurchaseResult', {
        success: false,
        productId: params.productId,
        message: `购买失败: ${e.code}, ${e.message}`
      });
      result.error(e.code.toString(), e.message, null);
    }
  }

  // 查询已购商品
  async queryPurchases(result: MethodResult): Promise<void> {
    try {
      const context = this.plugin.getContext();
      const productTypes = [
        iap.ProductType.CONSUMABLE,
        iap.ProductType.NONCONSUMABLE,
        iap.ProductType.AUTORENEWABLE,
        iap.ProductType.NONRENEWABLE
      ];

      const purchases: PurchasedProductInfo[] = [];

      for (const type of productTypes) {
        try {
          const param: iap.QueryPurchasesParameter = {
            productType: type,
            queryType: iap.PurchaseQueryType.ALL
          };

          const res = await iap.queryPurchases(context, param);
          const purchaseDataList = res.purchaseDataList || [];

          for (const data of purchaseDataList) {
            const parsedData = JSON.parse(data) as PurchaseData;
            const jwsOrder = parsedData.jwsPurchaseOrder;

            if (!jwsOrder) {
              continue;
            }

            const orderStr = JWSUtil.decodeJwsObj(jwsOrder);
            const orderPayload = JSON.parse(orderStr) as PurchaseOrderPayload;

            purchases.push({
              orderId: orderPayload.purchaseOrderId,
              productId: orderPayload.productId,
              productType: Number(orderPayload.productType),
              purchaseTime: new Date(orderPayload.purchaseTime).toLocaleString(),
              status: orderPayload.finishStatus === '1' ? '已完成' : '未完成'
            });
          }
        } catch (err) {
          console.error(`查询已购商品失败: ${JSON.stringify(err)}`);
        }
      }

      this.plugin.sendEvent('onPurchasesQueried', { purchases, success: true });
      result.success(null);
    } catch (err) {
      const e = err as BusinessError;
      result.error(e.code.toString(), e.message, null);
    }
  }
}

flutter端

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

class HuaweiIapPage extends StatefulWidget {
  const HuaweiIapPage({Key? key}) : super(key: key);

  @override
  State<HuaweiIapPage> createState() => _HuaweiIapPageState();
}

class _HuaweiIapPageState extends State<HuaweiIapPage> {
  // 统一MethodChannel实例,与原生端保持一致
  final MethodChannel _iapChannel =
      const MethodChannel('com.nobook.payment/huawei_iap');

  // 状态管理
  bool _isProcessing = false;
  String _statusMessage = '准备就绪';
  List<Map<String, dynamic>> _products = [];
  List<Map<String, dynamic>> _purchases = [];
  bool _showPurchases = false;

  @override
  void initState() {
    super.initState();
    // 初始化监听器
    _initListener();
    // 初始化IAP
    _initialize();
  }

  // 初始化事件监听器
  void _initListener() {
    _iapChannel.setMethodCallHandler(_handleIapEvent);
  }

  // 初始化IAP环境
  Future<void> _initialize() async {
    _setProcessingState(true, '初始化IAP环境...');
    try {
      // 调用原生端初始化方法
      await _iapChannel.invokeMethod('init');
    } on PlatformException catch (e) {
      _updateStatus('初始化失败: ${e.message}', isError: true);
    }
  }

  // 处理原生端发送的事件
  Future<dynamic> _handleIapEvent(MethodCall call) async {
    switch (call.method) {
      case 'onEnvironmentChecked':
        final bool success = call.arguments['success'] as bool;
        if (success) {
          _updateStatus('环境检查通过');
          _queryProducts();
        } else {
          _updateStatus('环境不支持IAP: ${call.arguments['message']}',
              isError: true);
        }
        break;

      case 'onUnfinishedOrdersQueried':
        _updateStatus('未完成订单已处理');
        break;

      case 'onProductsQueried':
        setState(() {
          _products = List<Map<String, dynamic>>.from(call.arguments['products']);
          _isProcessing = false;
        });
        _updateStatus('获取到${_products.length}个商品');
        break;

      case 'onPurchaseResult':
        final bool success = call.arguments['success'] as bool;
        final String productId = call.arguments['productId'] as String;

        if (success) {
          _updateStatus('购买成功: $productId');
          _queryPurchases();
        } else {
          _updateStatus('购买失败: ${call.arguments['message']}', isError: true);
        }
        break;

      case 'onPurchasesQueried':
        setState(() {
          _purchases = List<Map<String, dynamic>>.from(call.arguments['purchases']);
          _isProcessing = false;
        });
        _updateStatus('查询到${_purchases.length}条购买记录');
        break;

      case 'onDeliveryCompleted':
        final String orderId = call.arguments['orderId'] as String;
        _updateStatus('订单已发货: $orderId');
        break;
    }
    return null;
  }

  // 查询商品列表
  Future<void> _queryProducts() async {
    _updateStatus('正在查询商品...');
    try {
      await _iapChannel.invokeMethod('queryProducts');
    } on PlatformException catch (e) {
      _updateStatus('查询商品失败: ${e.message}', isError: true);
    }
  }

  // 购买商品
  Future<void> _purchaseProduct(String productId, int productType) async {
    _updateStatus('正在购买: $productId...');
    try {
      await _iapChannel.invokeMethod(
        'purchaseProduct',
        {
          'productId': productId,
          'productType': productType,
        },
      );
    } on PlatformException catch (e) {
      _updateStatus('购买请求失败: ${e.message}', isError: true);
    }
  }

  // 查询已购商品
  Future<void> _queryPurchases() async {
    _updateStatus('正在查询购买记录...');
    try {
      await _iapChannel.invokeMethod('queryPurchases');
    } on PlatformException catch (e) {
      _updateStatus('查询失败: ${e.message}', isError: true);
    }
  }

  // 更新状态信息(封装状态更新逻辑)
  void _updateStatus(String message, {bool isError = false}) {
    setState(() {
      _statusMessage = message;
      if (isError) {
        _isProcessing = false;
      }
    });
  }

  // 设置处理中状态
  void _setProcessingState(bool isProcessing, String message) {
    setState(() {
      _isProcessing = isProcessing;
      _statusMessage = message;
    });
  }

  // 获取商品类型名称
  String _getProductTypeName(int type) {
    switch (type) {
      case 0:
        return '消耗型';
      case 1:
        return '非消耗型';
      case 2:
        return '自动续期订阅';
      case 3:
        return '非续期订阅';
      default:
        return '未知类型';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('华为应用内支付'),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => Navigator.pop(context),
        ),
      ),
      body: Column(
        children: [
          // 状态信息展示区
          Container(
            padding: const EdgeInsets.all(16),
            color: Colors.grey[200],
            child: Row(
              children: [
                if (_isProcessing)
                  const Padding(
                    padding: EdgeInsets.only(right: 8),
                    child: SizedBox(
                      width: 20,
                      height: 20,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    ),
                  ),
                Expanded(
                  child: Text(
                    _statusMessage,
                    style: TextStyle(
                      color: _statusMessage.contains('失败')
                          ? Colors.red
                          : Colors.black,
                    ),
                  ),
                ),
              ],
            ),
          ),

          // 操作按钮区
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                ElevatedButton(
                  onPressed: _isProcessing ? null : _initialize,
                  child: const Text('重新初始化'),
                ),
                ElevatedButton(
                  onPressed: _isProcessing ? null : _queryProducts,
                  child: const Text('刷新商品'),
                ),
                ElevatedButton(
                  onPressed: _isProcessing ? null : _queryPurchases,
                  child: const Text('查询购买记录'),
                ),
                ElevatedButton(
                  onPressed: () =>
                      setState(() => _showPurchases = !_showPurchases),
                  child: Text(_showPurchases ? '显示商品' : '显示购买记录'),
                ),
              ],
            ),
          ),

          // 内容展示区
          Expanded(
            child: _showPurchases ? _buildPurchasesList() : _buildProductsList(),
          ),
        ],
      ),
    );
  }

  // 构建商品列表
  Widget _buildProductsList() {
    if (_products.isEmpty) {
      return const Center(child: Text('暂无商品信息'));
    }

    return ListView.builder(
      itemCount: _products.length,
      itemBuilder: (context, index) {
        final product = _products[index];
        return ListTile(
          leading: const Icon(Icons.shopping_bag, size: 36),
          title: Text(product['productName']),
          subtitle: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(product['description']),
              Text('价格: ${product['price']}'),
              Text('类型: ${_getProductTypeName(product['productType'])}'),
            ],
          ),
          trailing: ElevatedButton(
            onPressed: _isProcessing
                ? null
                : () => _purchaseProduct(
                    product['productId'], product['productType']),
            child: const Text('购买'),
          ),
        );
      },
    );
  }

  // 构建购买记录列表
  Widget _buildPurchasesList() {
    if (_purchases.isEmpty) {
      return const Center(child: Text('暂无购买记录'));
    }

    return ListView.builder(
      itemCount: _purchases.length,
      itemBuilder: (context, index) {
        final purchase = _purchases[index];
        return ListTile(
          leading: const Icon(Icons.receipt, size: 36),
          title: Text('订单: ${purchase['orderId']}'),
          subtitle: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('商品: ${purchase['productId']}'),
              Text('时间: ${purchase['purchaseTime']}'),
              Text('状态: ${purchase['status']}'),
              Text('类型: ${_getProductTypeName(purchase['productType'])}'),
            ],
          ),
        );
      },
    );
  }
}

他一直是初始化失败:“初始化失败:BusinessError 1001860001:System iinternal error”


更多关于HarmonyOS鸿蒙Next中flutter与原生通信出现了问题的实战教程也可以访问 https://www.itying.com/category-92-b0.html

2 回复

在HarmonyOS Next中,Flutter与原生通信问题通常涉及Platform Channels的实现。确保Flutter端MethodChannel名称与原生端完全一致。鸿蒙侧需使用ohos.ability上下文正确注册MethodHandler,并检查数据序列化格式(JSON)。常见问题包括线程冲突(需使用鸿蒙的TaskDispatcher切换主线程)、权限未声明或ArkCompiler对JSI调用的限制。

更多关于HarmonyOS鸿蒙Next中flutter与原生通信出现了问题的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


从代码和错误信息来看,Flutter与原生通信的问题可能出在IAP初始化环节。以下是关键点分析:

  1. 错误代码1001860001表明是系统内部错误,通常与IAP服务未正确初始化或环境配置有关

  2. 常见问题点:

  • 检查项目是否已正确配置IAP能力
  • 确认应用的包名与AGC平台配置一致
  • 验证签名证书是否匹配
  1. 代码层面建议检查:
// 确保context获取正确
this.context = binding.getApplicationContext() as common.UIAbilityContext;
if(!this.context) {
    result.error('100', '上下文无效', null);
    return;
}
  1. 初始化流程建议添加环境检查:
async initializeIap(result: MethodResult): Promise<void> {
    try {
        const envStatus = await iap.queryEnvironmentStatus(this.context);
        if(envStatus !== iap.EnvironmentStatus.SUPPORTED) {
            throw new Error('环境不支持IAP');
        }
        // 继续其他初始化...
    } catch(err) {
        console.error(`初始化失败: ${err.message}`);
        result.error('100', err.message, null);
    }
}

建议先验证基础IAP环境配置是否正确,再调试通信问题。

回到顶部