React Native integration

A native module with an event-driven challenge fetch, plus the TypeScript wrapper.

Step 1: Add dependency

In android/app/build.gradle:

dependencies {
    implementation "io.github.blinc-technologies:blinc-pos-sdk-ui:0.1.2"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.22"
}

Step 2: Create native module

package com.your.app

import com.blinc.blincpos.BlincPosSdk
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class BlincPosModule(reactContext: ReactApplicationContext)
    : ReactContextBaseJavaModule(reactContext) {

    private val scope                = CoroutineScope(Dispatchers.Main)
    private var pendingChallenge: CompletableDeferred<String?>? = null
    private val terminalId           = "T0001"
    private val merchantId           = "00000001"
    private val paymentFacilitatorId = 1L

    override fun getName(): String = "BlincPosModule"

    @ReactMethod
    fun provideChallenge(challenge: String?) {
        pendingChallenge?.complete(challenge)
    }

    @ReactMethod
    fun startTransaction(amount: Double, metadata: String, method: String, promise: Promise) {
        val activity = currentActivity as? MainActivity
        if (activity == null) {
            promise.reject("ConfigError", "Activity not available")
            return
        }
        activity.activePromise = promise

        scope.launch {
            val registered = BlincPosSdk.ensureRegistered(
                context              = reactApplicationContext,
                terminalId           = terminalId,
                merchantId           = merchantId,
                paymentFacilitatorID = paymentFacilitatorId,
                environment          = Environment.TEST // Environment.LIVE for prod
                challengeProvider    = {
                    val deferred = CompletableDeferred<String?>()
                    pendingChallenge = deferred
                    reactApplicationContext
                        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
                        .emit("BlincFetchChallenge", null)
                    deferred.await()
                }
            )
            if (!registered) {
                promise.reject("RegistrationError", "Could not register terminal")
                activity.activePromise = null
                return@launch
            }
            if (method == "QR") {
                BlincPosSdk.startTransactionWithQR(
                    context = activity, amount = amount.toLong(), metadata = metadata
                )
            } else {
                BlincPosSdk.startTransactionWithNFC(
                    context = activity, amount = amount.toLong(), metadata = metadata
                )
            }
        }
    }
}

Step 3: Update MainActivity

class MainActivity : ReactActivity() {

    var activePromise: Promise? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        BlincPosSdk.registerLauncher(this) { result ->
            activePromise?.let { promise ->
                val map = Arguments.createMap()
                map.putString("status",        result.status.name)
                map.putString("transactionId", result.transactionId)
                map.putString("message",       result.message)
                map.putString("currency",      result.currency)
                result.amount?.let { map.putDouble("amount", it.toDouble()) }
                promise.resolve(map)
                activePromise = null
            }
        }
    }
}

Step 4: Register the module

Add BlincPosModule to your ReactPackage list in MainApplication.kt.

TypeScript usage

import { NativeModules, NativeEventEmitter } from 'react-native';

const { BlincPosModule } = NativeModules;
const emitter = new NativeEventEmitter(BlincPosModule);

emitter.addListener('BlincFetchChallenge', async () => {
  try {
    const challenge = await YourBackendApiClient.fetchChallenge();
    BlincPosModule.provideChallenge(challenge);
  } catch {
    BlincPosModule.provideChallenge(null);
  }
});

export const BlincSDK = {
  pay: async (amountKobo: number, method: 'NFC' | 'QR', metadata: string = '{}') => {
    try {
      return await BlincPosModule.startTransaction(amountKobo, metadata, method);
    } catch (e) {
      console.error(e);
      throw e;
    }
  }
};

// Usage
const result = await BlincSDK.pay(199900, 'NFC', '{"orderId":"ORD-42"}');

Did this page help you?