You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过React Native原生模块将Java监听的支付事件数据传至JS

React Native安卓刷卡机支付事件监听方案

问题背景

我正在开发基于Expo Bare Workflow的React Native应用,该应用运行在安卓刷卡机上。在支付过程中需要监听插卡、输入密码、交易批准等事件,需实现Java原生代码收集事件数据,并通过React Native原生模块在JavaScript中获取这些事件。

原Java核心模块代码

package com.myapp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import android.util.Log;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPag;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagAppIdentification;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventData;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventListener;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagInitializationResult;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagPaymentData;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagActivationData;

public class PlugPagModule extends ReactContextBaseJavaModule {
    private final ReactApplicationContext reactContext;

    public PlugPagModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    private final int TYPE_CREDITO = 1;
    public String transactionsEvents = "";

    @NonNull
    @Override
    public String getName() {
        return "PlugPagModule";
    }

    @ReactMethod
    public void startPayment() {
        // 定义支付数据
        PlugPagPaymentData paymentData = new PlugPagPaymentData(TYPE_CREDITO,25000,PlugPag.INSTALLMENT_TYPE_A_VISTA,1,"CODVENDA");

        // 创建应用标识
        PlugPagAppIdentification appIdentification = new PlugPagAppIdentification("MeuApp", "1.0.7");

        // 创建PlugPag实例
        PlugPag plugpag = new PlugPag(reactContext, appIdentification);

        // 激活终端并发起支付
        PlugPagInitializationResult initResult = plugpag.initializeAndActivatePinpad(new PlugPagActivationData("403938"));

        if(initResult.getResult() == PlugPag.RET_OK) {
            plugpag.setEventListener(new PlugPagEventListener() {
                @Override
                public void onEvent(@NonNull PlugPagEventData plugPagEventData) {
                    Log.d("onEvent", String.valueOf(plugPagEventData.getCustomMessage()));
                    transactionsEvents = plugPagEventData.getCustomMessage();
                }
            });

            plugpag.doPayment(paymentData);
        }
    }
}

原React代码

import React from 'react';
import { NativeModules, NativeEventEmitter } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { MaterialIcons } from '@expo/vector-icons';
import { useTheme } from 'styled-components';

import { Button } from '../../components/Button';

import {
  Container,
  Header,
  BackButtonContainer,
  TitleHeader,
} from './styles';

interface NavigationProps {
  goBack(): void;
}

export function PayCommandWithCard() {
  const { PlugPagModule } = NativeModules;
  
  const theme = useTheme();
  const navigation = useNavigation<NavigationProps>();

  const events = new NativeEventEmitter(PlugPagModule.transactionsEvents);
  console.log(events);
  

  function handleBack() {
    navigation.goBack();
  }

  async function handlePayment() {
    await PlugPagModule.startPayment();
  }

  return (
    <Container>
      <Header>
        <BackButtonContainer>
          <Button
            color="secondary"
            rounded
            onPress={handleBack}
          >
            <MaterialIcons
              name="arrow-back"
              size={20}
              color={theme.colors.primary}
            />
          </Button>
        </BackButtonContainer>

        <TitleHeader>Pagar Comanda</TitleHeader>
      </Header>

      <Button
        title="test"
        onPress={handlePayment}
      />
    </Container>
  );
}

解决方案

1. 修改Java原生模块:实时推送事件到JS端

当前代码仅将事件存储在字符串变量中,无法主动通知JS端。需要通过React Native的DeviceEventManagerModule将事件数据实时发送到JavaScript层。

修改后的Java代码:

package com.myapp;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPag;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagAppIdentification;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventData;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventListener;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagInitializationResult;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagPaymentData;
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagActivationData;

public class PlugPagModule extends ReactContextBaseJavaModule {
    private final ReactApplicationContext reactContext;
    private static final String PAYMENT_EVENT_NAME = "onPaymentEvent";

    public PlugPagModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    private final int TYPE_CREDITO = 1;

    @NonNull
    @Override
    public String getName() {
        return "PlugPagModule";
    }

    // 发送事件到JS端的工具方法
    private void sendEvent(String eventName, WritableMap params) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }

    @ReactMethod
    public void startPayment() {
        PlugPagPaymentData paymentData = new PlugPagPaymentData(TYPE_CREDITO,25000,PlugPag.INSTALLMENT_TYPE_A_VISTA,1,"CODVENDA");
        PlugPagAppIdentification appIdentification = new PlugPagAppIdentification("MeuApp", "1.0.7");
        PlugPag plugpag = new PlugPag(reactContext, appIdentification);

        PlugPagInitializationResult initResult = plugpag.initializeAndActivatePinpad(new PlugPagActivationData("403938"));

        if(initResult.getResult() == PlugPag.RET_OK) {
            plugpag.setEventListener(new PlugPagEventListener() {
                @Override
                public void onEvent(@NonNull PlugPagEventData plugPagEventData) {
                    // 构造可被JS识别的事件数据
                    WritableMap eventData = new WritableNativeMap();
                    eventData.putString("customMessage", plugPagEventData.getCustomMessage());
                    eventData.putInt("eventCode", plugPagEventData.getEventCode());
                    eventData.putString("eventMessage", plugPagEventData.getMessage());
                    
                    // 发送事件到JS端
                    sendEvent(PAYMENT_EVENT_NAME, eventData);
                }
            });

            plugpag.doPayment(paymentData);
        }
    }
}

关键修改点:

  • 移除无用的transactionsEvents变量
  • 添加sendEvent工具方法,通过DeviceEventManagerModule推送事件
  • 在onEvent回调中,将PlugPagEventData的关键字段封装成WritableMap推送到JS端

2. 修改React Native代码:监听原生事件

当前代码创建NativeEventEmitter的方式错误,需要传入原生模块实例,并正确监听事件。

修改后的React代码:

import React, { useEffect, useState } from 'react';
import { NativeModules, NativeEventEmitter } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { MaterialIcons } from '@expo/vector-icons';
import { useTheme } from 'styled-components';

import { Button } from '../../components/Button';

import {
  Container,
  Header,
  BackButtonContainer,
  TitleHeader,
} from './styles';

interface NavigationProps {
  goBack(): void;
}

// 定义事件数据类型
interface PaymentEventData {
  customMessage: string;
  eventCode: number;
  eventMessage: string;
}

export function PayCommandWithCard() {
  const { PlugPagModule } = NativeModules;
  const eventEmitter = new NativeEventEmitter(PlugPagModule);
  
  const theme = useTheme();
  const navigation = useNavigation<NavigationProps>();
  const [currentEvent, setCurrentEvent] = useState<PaymentEventData | null>(null);

  useEffect(() => {
    // 监听原生发送的支付事件
    const eventListener = eventEmitter.addListener('onPaymentEvent', (event: PaymentEventData) => {
      console.log('收到支付事件:', event);
      setCurrentEvent(event);
      // 根据eventCode处理不同场景:插卡、输入密码、交易批准等
      switch(event.eventCode) {
        case 1001:
          // 处理插卡事件
          break;
        case 1002:
          // 处理输入密码事件
          break;
        case 2000:
          // 处理交易批准事件
          break;
        default:
          break;
      }
    });

    // 组件卸载时移除监听器,避免内存泄漏
    return () => {
      eventListener.remove();
    };
  }, [eventEmitter]);

  function handleBack() {
    navigation.goBack();
  }

  async function handlePayment() {
    await PlugPagModule.startPayment();
  }

  return (
    <Container>
      <Header>
        <BackButtonContainer>
          <Button
            color="secondary"
            rounded
            onPress={handleBack}
          >
            <MaterialIcons
              name="arrow-back"
              size={20}
              color={theme.colors.primary}
            />
          </Button>
        </BackButtonContainer>

        <TitleHeader>Pagar Comanda</TitleHeader>
      </Header>

      {/* 显示当前事件信息 */}
      {currentEvent && (
        <div>
          <p>事件消息: {currentEvent.eventMessage}</p>
          <p>自定义消息: {currentEvent.customMessage}</p>
        </div>
      )}

      <Button
        title="发起支付"
        onPress={handlePayment}
      />
    </Container>
  );
}

关键修改点:

  • 正确创建NativeEventEmitter实例,传入PlugPagModule
  • 使用useEffect添加事件监听器,并在组件卸载时移除
  • 定义PaymentEventData类型规范事件数据结构
  • 根据eventCode区分不同支付阶段的事件(插卡、输密、交易批准等)
  • 添加事件展示UI,方便调试

内容的提问来源于stack exchange,提问作者Bruno Azevedo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:45:41