react-native-sunmi-pay-sdk
v1.0.3
Published
React Native wrapper for Sunmi Pay SDK - Card reader, EMV, PinPad and more
Downloads
18
Maintainers
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-clientStep 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:androidFor EAS Build:
npm install -g eas-cli
eas build --profile development --platform androidSee Expo docs for more details.
Installation
npm install @sunmi/react-native-sunmi-pay-sdk
# or
yarn add @sunmi/react-native-sunmi-pay-sdkAndroid 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-androidLinking (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-sdkQuick 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 beepcontrolLed(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 cardMifare 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 linesEvent 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 insertedCARD_REMOVE: Card was removedPIN_INPUT: PIN digit enteredEMV_CONFIRM_CARD: EMV card number confirmation neededEMV_REQUEST_AMOUNT: EMV amount input neededEMV_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:
- Make sure your device is a Sunmi payment terminal
- Ensure the SunmiPayHardwareService is installed and running
- Grant all necessary permissions in Android settings
- Run your React Native app
Troubleshooting
"Service not found" error
- Make sure you're running on a real Sunmi device
- Add the
QUERY_ALL_PACKAGESpermission 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.
