npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-native-sunmi-pay-sdk

v1.0.3

Published

React Native wrapper for Sunmi Pay SDK - Card reader, EMV, PinPad and more

Downloads

18

Readme

@sunmi/react-native-sunmi-pay-sdk

A comprehensive React Native wrapper for the Sunmi Pay SDK, providing seamless integration with Sunmi payment devices for card reading, EMV transactions, PIN entry, encryption, and printing.

Features

  • Card Reading: Support for magnetic stripe, IC (chip), NFC/contactless cards
  • EMV Processing: Full EMV L2 transaction processing
  • PIN Entry: Secure PIN pad integration with encryption
  • Security: Key management, encryption/decryption, MAC calculation, RSA/SM2 support
  • Printer: Built-in printer support for receipts, barcodes, QR codes
  • TypeScript: Full TypeScript support with comprehensive type definitions
  • Event-driven: React to card insertions, PIN input, and EMV events
  • Promise-based API: Modern async/await syntax

Requirements

  • React Native >= 0.60
  • Android 6.0+ (API level 21+)
  • Sunmi payment device with PayHardwareService installed
  • Sunmi Pay SDK version 2.0.07+

Expo Setup

⚠️ Not compatible with Expo Go (contains custom native code)

Works with Expo using development builds:

Step 1: Install the package

npx expo install @sunmi/react-native-sunmi-pay-sdk expo-dev-client

Step 2: Add Android permission to app.json or app.config.js

{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "extraManifestAttributes": {
              "xmlns:tools": "http://schemas.android.com/tools"
            }
          }
        }
      ]
    ],
    "android": {
      "permissions": [
        "android.permission.QUERY_ALL_PACKAGES"
      ]
    }
  }
}

Or use a config plugin in app.config.js:

export default {
  expo: {
    plugins: [
      [
        "expo-build-properties",
        {
          android: {
            extraManifestAttributes: {
              "xmlns:tools": "http://schemas.android.com/tools"
            }
          }
        }
      ]
    ],
    android: {
      permissions: ["android.permission.QUERY_ALL_PACKAGES"]
    }
  }
};

Step 3: Prebuild and run

npx expo prebuild --clean
npx expo run:android

For EAS Build:

npm install -g eas-cli
eas build --profile development --platform android

See Expo docs for more details.

Installation

npm install @sunmi/react-native-sunmi-pay-sdk
# or
yarn add @sunmi/react-native-sunmi-pay-sdk

Android Configuration

For React Native 0.60+: The package auto-links and the required QUERY_ALL_PACKAGES permission should merge automatically from the package's manifest. ✅

If auto-merge doesn't work (rare cases), manually add to android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    
    <!-- Required for Android 11+ to bind to Sunmi Pay Service -->
    <uses-permission 
        android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />
</manifest>

Note: After installation, rebuild your app:

cd android && ./gradlew clean
cd .. && npx react-native run-android

Linking (React Native < 0.60)

If you're using React Native < 0.60, you need to manually link:

react-native link @sunmi/react-native-sunmi-pay-sdk

Quick Start

import SunmiPay, { CardSlotType } from '@sunmi/react-native-sunmi-pay-sdk';

// Initialize the SDK first
async function initializeSunmiSDK() {
  try {
    const success = await SunmiPay.initialize();
    if (success) {
      console.log('Sunmi Pay SDK initialized successfully');
    }
  } catch (error) {
    console.error('Failed to initialize SDK:', error);
  }
}

// Check for a card
async function readCard() {
  try {
    const cardInfo = await SunmiPay.checkCard({
      cardType: CardSlotType.ALL, // Accept all card types
      timeout: 60, // 60 seconds timeout
    });
    
    console.log('Card detected:', cardInfo);
    
    switch (cardInfo.cardType) {
      case 0: // Magnetic stripe
        console.log('Magnetic card:', cardInfo.track2);
        break;
      case 1: // IC card
        console.log('IC card ATR:', cardInfo.atr);
        break;
      case 2: // NFC card
        console.log('NFC card:', cardInfo.cardNumber);
        break;
    }
  } catch (error) {
    console.error('Card reading error:', error);
  }
}

API Documentation

Initialization

initialize(): Promise<boolean>

Initialize the Sunmi Pay SDK. Must be called before using any other methods.

const success = await SunmiPay.initialize();

isConnected(): Promise<boolean>

Check if SDK is connected to the payment service.

const connected = await SunmiPay.isConnected();

Basic Operations

getSystemInfo(): Promise<SystemInfo>

Get system information about the device.

const info = await SunmiPay.getSystemInfo();
console.log('Device:', info.model);
console.log('Serial:', info.serialNumber);
console.log('SDK Version:', info.sdkVersion);

beep(duration?: number): Promise<void>

Make the device beep.

await SunmiPay.beep(200); // 200ms beep

controlLed(config: LedConfig): Promise<void>

Control the LED lights.

await SunmiPay.controlLed({
  ledType: 0, // LED type
  onTime: 500, // On duration (ms)
  offTime: 500, // Off duration (ms)
  count: 3, // Number of blinks
});

Card Reading

checkCard(options: CheckCardOptions): Promise<CardInfo>

Wait for a card to be inserted, swiped, or tapped.

import { CardSlotType } from '@sunmi/react-native-sunmi-pay-sdk';

const cardInfo = await SunmiPay.checkCard({
  cardType: CardSlotType.ALL, // NFC, IC, or SWIPE
  timeout: 60, // seconds
});

cancelCheckCard(): Promise<void>

Cancel an ongoing card check operation.

await SunmiPay.cancelCheckCard();

sendApdu(command: ApduCommand): Promise<ApduResponse>

Send an APDU command to the card.

const response = await SunmiPay.sendApdu({
  cla: 0x00,
  ins: 0xA4,
  p1: 0x04,
  p2: 0x00,
  data: '315041592E5359532E4444463031', // hex string
  le: 0,
});

console.log('SW1:', response.sw1);
console.log('SW2:', response.sw2);
console.log('Data:', response.data);

cardOff(cardType: number): Promise<void>

Turn off the card (IC or NFC).

await SunmiPay.cardOff(1); // Turn off IC card

Mifare Card Operations

mifareAuth(blockId: number, keyType: number, key: string): Promise<boolean>

Authenticate to a Mifare card block.

const success = await SunmiPay.mifareAuth(
  4, // Block ID
  0x60, // Key A
  'FFFFFFFFFFFF' // Key (hex string)
);

mifareReadBlock(blockId: number): Promise<string>

Read a Mifare card block.

const data = await SunmiPay.mifareReadBlock(4);
console.log('Block data:', data);

mifareWriteBlock(blockId: number, data: string): Promise<void>

Write to a Mifare card block.

await SunmiPay.mifareWriteBlock(4, '00000000000000000000000000000000');

EMV Transactions

saveAid(aid: AidParam): Promise<void>

Save an Application Identifier (AID) parameter.

await SunmiPay.saveAid({
  aid: 'A0000000031010',
  aidLen: 7,
  selFlag: 0,
  priority: 1,
  targetPer: 0,
  maxTargetPer: 99,
  floorLimitCheck: 1,
  randTransSel: 1,
  velocityCheck: 1,
  floorLimit: 0,
  threshold: 0,
  tacDefault: '0000000000',
  tacOnline: '0000000000',
  tacDenial: '0000000000',
  acquireId: '123456',
  dDol: '9F3704',
  tDol: '9F02065F2A029A039C0195059F3704',
  version: '008C',
});

saveCapk(capk: CapkParam): Promise<void>

Save a Certificate Authority Public Key (CAPK).

await SunmiPay.saveCapk({
  rid: 'A000000003',
  keyID: 0x92,
  hashInd: 0x01,
  arithInd: 0x01,
  modul: 'B0...',
  exponent: '03',
  expDate: '20251231',
  checkSum: '...',
});

setTerminalParam(param: EmvTerminalParam): Promise<void>

Set terminal parameters for EMV transactions.

await SunmiPay.setTerminalParam({
  capability: 'E0F8C8',
  extraCapability: 'F000F0A001',
  countryCode: '0840', // USA
  currencyCode: '0840', // USD
  merchantId: '123456789012345',
  merchantCategoryCode: '5999',
  merchantNameLocation: 'My Store',
  terminalId: 'TERM0001',
  terminalType: 0x22,
  transactionCurrencyExp: 2,
  referCurrencyCode: '0840',
  referCurrencyExp: 2,
  referCurrencyConv: 0,
});

startEmvProcess(transData: EmvTransactionData): Promise<EmvResult>

Start an EMV transaction.

import { TransactionType } from '@sunmi/react-native-sunmi-pay-sdk';

const result = await SunmiPay.startEmvProcess({
  amount: '1000', // $10.00 in cents
  cashbackAmount: '0',
  transactionType: TransactionType.GOODS,
  date: '20231009',
  time: '123045',
});

if (result.resultCode === 0) {
  console.log('Transaction approved');
  console.log('Card number:', result.cardNumber);
  console.log('AID:', result.aid);
  console.log('TC:', result.tc);
} else {
  console.log('Transaction declined:', result.description);
}

PIN Entry

initPinPad(): Promise<void>

Initialize the PIN pad.

await SunmiPay.initPinPad();

startPinInput(config: PinPadConfig): Promise<PinInputResult>

Start PIN entry.

import { PinPadMode, KeyType, AlgorithmType } from '@sunmi/react-native-sunmi-pay-sdk';

// Listen for PIN input events
SunmiPay.addEventListener('onPinInput', (length) => {
  console.log(`PIN length: ${length}`);
});

const result = await SunmiPay.startPinInput({
  mode: PinPadMode.USE_PINPAD_INTERNAL,
  keyIndex: 0,
  timeout: 60,
  minLength: 4,
  maxLength: 6,
});

console.log('PIN block:', result.pinBlock);

cancelPinInput(): Promise<void>

Cancel ongoing PIN entry.

await SunmiPay.cancelPinInput();

Security & Encryption

saveMasterKey(keyIndex: number, key: string, checkValue: string): Promise<void>

Save a master key (MKSK).

await SunmiPay.saveMasterKey(
  0, // Key index
  '0123456789ABCDEF0123456789ABCDEF', // 3DES key (hex)
  '1234567890ABCDEF' // Check value
);

saveWorkKey(params: UpdateKeyParams): Promise<void>

Save a working key (derived from master key).

import { KeyType, AlgorithmType } from '@sunmi/react-native-sunmi-pay-sdk';

await SunmiPay.saveWorkKey({
  keyType: KeyType.PIN_KEY,
  keyIndex: 0,
  keyData: '...', // Encrypted key data
  checkValue: '...',
});

encryptData(params: EncryptionParams): Promise<EncryptionResult>

Encrypt data using a saved key.

import { AlgorithmType } from '@sunmi/react-native-sunmi-pay-sdk';

const result = await SunmiPay.encryptData({
  keyIndex: 0,
  algorithmType: AlgorithmType.TDES,
  data: '0123456789ABCDEF',
  iv: '0000000000000000', // Optional, for CBC mode
});

console.log('Encrypted data:', result.data);

decryptData(params: EncryptionParams): Promise<EncryptionResult>

Decrypt data using a saved key.

const result = await SunmiPay.decryptData({
  keyIndex: 0,
  algorithmType: AlgorithmType.TDES,
  data: '...',
});

console.log('Decrypted data:', result.data);

calculateMac(params: MacParams): Promise<MacResult>

Calculate a Message Authentication Code (MAC).

const result = await SunmiPay.calculateMac({
  keyIndex: 0,
  algorithmType: AlgorithmType.TDES,
  data: '0123456789ABCDEF',
  macType: 1, // X9.19
});

console.log('MAC:', result.mac);

Printer

getPrinterStatus(): Promise<PrinterStatus>

Get printer status.

const status = await SunmiPay.getPrinterStatus();
console.log('Paper status:', status.paperStatus);

printText(textConfig: PrintText): Promise<void>

Print text.

await SunmiPay.printText({
  text: 'Hello, World!',
  fontSize: 24,
  alignment: 1, // Center
  bold: true,
});

printQrCode(content: string, size: number): Promise<void>

Print a QR code.

await SunmiPay.printQrCode('https://example.com', 8);

feedPaper(lines: number): Promise<void>

Feed paper.

await SunmiPay.feedPaper(3); // Feed 3 lines

Event Listeners

Listen to SDK events:

import { SunmiPayEvents } from '@sunmi/react-native-sunmi-pay-sdk';

// Card insertion event
const subscription = SunmiPay.addEventListener(
  SunmiPayEvents.CARD_INSERT,
  (event) => {
    console.log('Card inserted:', event.cardType);
  }
);

// Remove listener when done
subscription.remove();

// Or remove all listeners
SunmiPay.removeAllListeners();

Available events:

  • CARD_INSERT: Card was inserted
  • CARD_REMOVE: Card was removed
  • PIN_INPUT: PIN digit entered
  • EMV_CONFIRM_CARD: EMV card number confirmation needed
  • EMV_REQUEST_AMOUNT: EMV amount input needed
  • EMV_ONLINE_PROCESS: EMV online authorization needed

Complete Example

import React, { useEffect, useState } from 'react';
import { View, Button, Text, Alert } from 'react-native';
import SunmiPay, {
  CardSlotType,
  TransactionType,
} from '@sunmi/react-native-sunmi-pay-sdk';

function PaymentScreen() {
  const [initialized, setInitialized] = useState(false);

  useEffect(() => {
    initSDK();
  }, []);

  async function initSDK() {
    try {
      const success = await SunmiPay.initialize();
      setInitialized(success);
      if (success) {
        // Play success beep
        await SunmiPay.beep(100);
      }
    } catch (error) {
      Alert.alert('Error', 'Failed to initialize SDK');
    }
  }

  async function processPayment() {
    try {
      // 1. Read card
      Alert.alert('Info', 'Please insert, swipe or tap card');
      
      const cardInfo = await SunmiPay.checkCard({
        cardType: CardSlotType.ALL,
        timeout: 60,
      });

      // 2. Start EMV transaction
      const result = await SunmiPay.startEmvProcess({
        amount: '1000', // $10.00
        transactionType: TransactionType.GOODS,
      });

      if (result.resultCode === 0) {
        Alert.alert('Success', 'Payment approved!');
        
        // 3. Print receipt
        await SunmiPay.printText({
          text: 'Payment Receipt',
          fontSize: 32,
          alignment: 1,
          bold: true,
        });
        await SunmiPay.printText({
          text: `Amount: $10.00`,
          fontSize: 24,
          alignment: 0,
          bold: false,
        });
        await SunmiPay.feedPaper(3);
      } else {
        Alert.alert('Error', 'Payment declined');
      }
    } catch (error) {
      Alert.alert('Error', error.message);
    }
  }

  return (
    <View style={{ padding: 20 }}>
      <Text>SDK Status: {initialized ? 'Ready' : 'Not Ready'}</Text>
      <Button
        title="Process Payment"
        onPress={processPayment}
        disabled={!initialized}
      />
    </View>
  );
}

export default PaymentScreen;

TypeScript Support

This package is written in TypeScript and provides comprehensive type definitions. Import types as needed:

import type {
  CardInfo,
  CardType,
  EmvResult,
  PinInputResult,
  SystemInfo,
  // ... and many more
} from '@sunmi/react-native-sunmi-pay-sdk';

Error Handling

All methods return promises that may reject with errors. Always use try-catch:

try {
  await SunmiPay.checkCard({ cardType: CardSlotType.ALL, timeout: 60 });
} catch (error) {
  if (error.code === 'TIMEOUT') {
    console.log('Card reading timed out');
  } else if (error.code === 'USER_CANCEL') {
    console.log('User cancelled');
  } else {
    console.error('Error:', error.message);
  }
}

Testing

To test on a real device:

  1. Make sure your device is a Sunmi payment terminal
  2. Ensure the SunmiPayHardwareService is installed and running
  3. Grant all necessary permissions in Android settings
  4. Run your React Native app

Troubleshooting

"Service not found" error

  • Make sure you're running on a real Sunmi device
  • Add the QUERY_ALL_PACKAGES permission to AndroidManifest.xml
  • Ensure SunmiPayHardwareService is installed and up to date

SDK not initializing

  • Check if the device has the payment service installed
  • Try rebooting the device
  • Contact Sunmi support for service installation

Card reading not working

  • Ensure the card slot/reader is not physically damaged
  • Try different card types
  • Check if another app is using the card reader

License

MIT

Support

For issues and feature requests, please file an issue on GitHub.

Credits

This package wraps the official Sunmi Pay SDK. For more information about Sunmi devices and SDKs, visit Sunmi Developer Portal.