Skip to main content

Package

Installation

npm install react-native-cloudcard
# or
yarn add react-native-cloudcard

Android Repository Setup

For Gradle < 7 (android/build.gradle):
allprojects {
  repositories {
    google()
    mavenCentral()
    maven {
      url = uri("https://sdk.sudo.africa/repository/maven-releases/")
      credentials {
        username = project.findProperty("maven.repo.username") ?: ""
        password = project.findProperty("maven.repo.password") ?: ""
      }
    }
  }
}
For Gradle 7+ (android/settings.gradle):
dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
    maven {
      url = uri("https://sdk.sudo.africa/repository/maven-releases/")
      credentials {
        username = providers.gradleProperty("maven.repo.username").orNull ?: System.getenv("NEXUS_USER")
        password = providers.gradleProperty("maven.repo.password").orNull ?: System.getenv("NEXUS_PASS")
      }
    }
  }
}
Set credentials in gradle.properties:
maven.repo.username=your_sudo_username
maven.repo.password=your_sudo_password
Or set environment variables:
export NEXUS_USER=your_sudo_username
export NEXUS_PASS=your_sudo_password

Android Manifest Configuration

Register the HCE service and receiver inside <application> in android/app/src/main/AndroidManifest.xml.
<service
  android:name="com.sudo.cloud_card.HCEService"
  android:enabled="true"
  android:exported="true"
  android:permission="android.permission.BIND_NFC_SERVICE">
  <intent-filter>
    <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
  <meta-data
    android:name="android.nfc.cardemulation.host_apdu_service"
    android:resource="@xml/apduservice" />
</service>

<receiver
  android:name="africa.sudo.cloudcard.reactnative.HceEventReceiver"
  android:enabled="true"
  android:exported="true">
  <intent-filter>
    <action android:name="${applicationId}.CLOUDCARD_EVENT" />
  </intent-filter>
</receiver>
Create android/app/src/main/res/xml/apduservice.xml:
<?xml version="1.0" encoding="utf-8"?>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/app_name"
    android:requireDeviceUnlock="false">
    <aid-group android:category="payment" android:description="@string/app_name" />
</host-apdu-service>

iOS Setup

Create .env in your project root:
SUDO_USER=your_sudo_username
SUDO_PASS=your_sudo_password
Run:
export $(grep -v '^#' .env | xargs)
TMPHOME=$(mktemp -d)
trap 'rm -rf "$TMPHOME"' EXIT
export HOME="$TMPHOME"
cat > "$HOME/.netrc" <<EOF_NETRC
machine sdk.sudo.africa
login $SUDO_USER
password $SUDO_PASS
EOF_NETRC
chmod 600 "$HOME/.netrc"
cd ios && pod install

Initialization

Initialize once near app startup.
import CloudCard from 'react-native-cloudcard';

export async function initializeCloudCard() {
  await CloudCard.init({
    isSandBox: true,
    onCardScanned: (event) => {
      console.log('scan started', event?.eventType ?? event?.event_type, event?.message);
    },
    onScanComplete: (event) => {
      console.log('scan done', event?.isSuccess ?? event?.is_success, event?.amount);
    },
  });
}
Environment:
  • isSandBox: true for sandbox
  • isSandBox: false for production

Health Checks

const isSupported = await CloudCard.isDeviceSupported();
const isNfcEnabled = await CloudCard.isNfcEnabled();
const isDefaultApp = await CloudCard.isDefaultPaymentApp();

if (isSupported && isNfcEnabled === true && isDefaultApp === false) {
  await CloudCard.launchDefaultPaymentAppSettings();
}

Optional Security Behavior

await CloudCard.setRequireAuth(true);

Register Card

You register cards with RegistrationData. Required fields:
  • walletId
  • paymentAppInstanceId
  • accountId
  • jwtToken
Optional fields:
  • secret
  • cardNumber
  • expiryDate
  • cardHolderName
import CloudCard from 'react-native-cloudcard';

const result = await CloudCard.registerCard({
  walletId: 'institution-id',
  paymentAppInstanceId: 'device-instance-id',
  accountId: 'card-or-account-id',
  jwtToken: 'onboarding-jwt-token',
  secret: 'optional-secret',
  cardNumber: '4111111111111111',
  expiryDate: '12/25',
  cardHolderName: 'John Doe',
});

if (result.status === 'SUCCESS') {
  console.log('Card registered');
} else {
  console.log(`Registration failed: ${result.message}`);
}

Digitalization Fetch Pattern

If you already have your own card-management system, you can build and pass your own RegistrationData directly to registerCard using the required fields walletId, paymentAppInstanceId, accountId, and jwtToken. If you do not manage that payload yourself, fetch the onboarding or digitalization payload from your backend first, then pass it to registerCard. For endpoint details, see Card Digitalization Reference.
const res = await fetch(
  `https://api.sandbox.sudo.cards/cards/digitalize/{id}`,
  {
    method: 'GET',
    headers: {
      Accept: 'text/plain',
      'Content-Type': 'application/json',
      platform: 'android',
      Authorization: accessToken,
    },
  }
);

const jwtToken = await res.text();

Card Management

Get Cards

import CloudCard from 'react-native-cloudcard';

const result = await CloudCard.getCards();
if (result.status === 'SUCCESS' && Array.isArray(result.data)) {
  result.data.forEach((card) => {
    console.log(card.id, card.maskedPan, `active=${card.isActive}`);
  });
}

Freeze or Unfreeze

await CloudCard.freezeUnfreezeCard({
  cardId,
  isFreeze: true,
  periodInDays: 7,
});
Set isFreeze: false to unfreeze.

Delete Card

await CloudCard.deleteCard(cardId);

Wipe Wallet

await CloudCard.wipeWallet();
Use this only after explicit user confirmation.

Manual Key Replenishment

const result = await CloudCard.manualKeyReplenishment();

Token Usage Summary

const result = await CloudCard.tokenSummary();
if (result.status === 'SUCCESS' && result.data) {
  console.log(
    `total=${result.data.totalTokens} balance=${result.data.tokensBalance}`
  );
}

QR and Transactions

Generate EMV QR

import CloudCard from 'react-native-cloudcard';

const result = await CloudCard.getEmvQr({
  cardId,
  amount: '000000000200',
  foregroundColorHex: '#000000',
  backgroundColorHex: '#FFFFFF',
});

if (result.status === 'SUCCESS' && result.data) {
  const uri = `data:image/png;base64,${result.data}`;
  console.log('QR ready', uri);
}
amount is in the smallest unit and typically zero-padded.

Read Saved Transactions

getSavedTransactions() returns recent cached transactions, up to 5.
const result = await CloudCard.getSavedTransactions();
if (result.status === 'SUCCESS' && Array.isArray(result.data)) {
  result.data.forEach((tx) => {
    console.log(tx.amount, tx.currency, tx.timestamp, tx.type);
  });
}

NFC and Settings

import CloudCard from 'react-native-cloudcard';

const isNfcEnabled = await CloudCard.isNfcEnabled();
const isDefaultPaymentApp = await CloudCard.isDefaultPaymentApp();

if (isNfcEnabled === true && isDefaultPaymentApp === false) {
  await CloudCard.launchDefaultPaymentAppSettings();
}

NFC Chip Indicator Helpers

await CloudCard.showNfcChipPosition({
  duration: 5000,
  colorHex: '#AF5298',
});

const position = await CloudCard.getNfcChipPosition();
console.log(position?.x, position?.y, position?.radius, position?.hint);

await CloudCard.hideNfcChipPosition();

Requirements

  • React Native 0.68+
  • Android API 24+
  • iOS 13.0+
NFC-enabled device is required for NFC payments, but not for QR.

Troubleshooting

Initialization Fails

  • Confirm SDK repository credentials are set correctly on Android.
  • Confirm the iOS .netrc auth step was completed before pod install.
  • Check that you called init() before card operations.
  • Rebuild the app after native dependency changes.
  • Run cd ios && pod install for iOS before rebuilding.

NFC Not Working

  • Confirm the device supports NFC.
  • Confirm NFC is enabled in system settings.
  • Confirm your app is set as default payment app when required.
  • Verify the HCE service and receiver entries in AndroidManifest.xml.
  • Confirm the receiver class is africa.sudo.cloudcard.reactnative.HceEventReceiver.

Card Registration Fails

  • Verify walletId, paymentAppInstanceId, accountId, and jwtToken.
  • Ensure the onboarding token is still valid.
  • Check that your backend is issuing tokens for the correct environment.

QR Generation Fails

  • Use a valid cardId.
  • Ensure amount is formatted correctly.

Empty Transaction List

  • getSavedTransactions() uses local cache and returns recent transactions only.
  • Complete at least one transaction flow first, then reload.

Support

Contact support@sudo.africa.