Logo
Android

Integration via App to App call

Integration via App to App call

For app-to-app launch mode, the Neurogine n2Tap SoftPOS App accepts android Intent for various requests.

We provide a wrapper library - n2applib that wraps the essential APIs and data classes for the ease of integration.


Prerequisite

Before start integrating with the Neurogine n2Tap SoftPOS app, you'll need:


Gradle setup

In your machine/ environment, set up the credential for downloading the sdk package:

# neurogine client registry
NEUROGINE_REGISTRY_LOGIN=neurogine-product-support
NEUROGINE_REGISTRY_TOKEN={token-value}

Please seek our customer support for the token.

In your project, (usually) the settings.gradle or settings.gradle.kts, setup the maven registry like the following:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()

        // Neurogine's maven registry
        maven {
            val NEUROGINE_REGISTRY_LOGIN: String? by settings
            val NEUROGINE_REGISTRY_TOKEN: String? by settings

            requireNotNull(NEUROGINE_REGISTRY_LOGIN) {
                """
                    Please set your Neurogine Github credential in `gradle.properties`.
                    On local machine,
                    ** DO NOT **
                    ** DO NOT **
                    ** DO NOT **
                    Do not put it in the project's file. (and accidentally commit and push)
                    ** DO **
                    Do set it in your machine's global (~/.gradle/gradle.properties)
                """.trimIndent()
            }
            requireNotNull(NEUROGINE_REGISTRY_TOKEN)

            name = "NeurogineMavenClientRegistry"
            url = uri("<your-neurogine-maven-registry-url>")
            credentials {
                username = NEUROGINE_REGISTRY_LOGIN
                password = NEUROGINE_REGISTRY_TOKEN
            }
        }
    }
}

dependencies {
    // ... other deps
    implementation("com.neurogine.app:poslib:1.0.0")
}

PosLib Setups

Setting Up AndroidManifest.xml

Android 11 (API level 30) and later require a <queries> element in the manifest to enable package visibility queries.

→ More on Android dev docs

Edit android/app/src/main/AndroidManifest.xml: add the <queries> element outside the <application> element, and add a <meta-data> entry inside <application> to record the SoftPOS package name for your environment.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Allow querying for the SoftPOS package -->
  <queries>
    <intent>
      <action android:name="android.intent.action.PROCESS_TEXT"/>
      <data android:mimeType="text/plain"/>
    </intent>
    <package android:name="com.neurogine.softpos.stage" />
  </queries>

  <application>
    <!-- Set SoftPOS package name for the environment -->
    <meta-data
      android:name="softposName"
      android:value="com.neurogine.softpos.stage" />

    <!-- existing application config -->
  </application>
</manifest>

Create the POS API

Pass the n2Tap package name into N2applib (provided by the poslib):

private val n2appLibApi = N2applib(N2TAP_PACKAGE_NAME)

Example helper to read a metadata value (for example softposName) from your app manifest:

fun getValue(context: Context, key: String): String? {
  return try {
    val appInfo = context.packageManager.getApplicationInfo(
      context.packageName,
      PackageManager.GET_META_DATA
    )
    appInfo.metaData?.getString(key)
  } catch (e: Exception) {
    null
  }
}

Check if SoftPOS is Installed

After updating the manifest and initializing the API, use the library to check whether the SoftPOS app is installed on the device:

// require a Context (e.g. Activity or Compose LocalContext)
n2appLibApi.isSoftPosInstalled(context)

Important Notes:

  • Update the package name to match your SoftPOS environment:
    • Use com.neurogine.softpos for production
    • Use com.neurogine.softpos.stage for staging/testing

Call gesture

The POS lib currently provides the ActivityResultContract for the following app operations:

  • Activation: to activate the app and load configs from the backend
  • WarmUp: to warm up the SoftPOS app, perform the runtime checking on device and application
  • Transaction: to perform a transaction like sale, void, or refund
  • Enquiry: to check the previous transaction status
  • Settlement: to settle all the batch under the merchant

Generally you'll want to register for the activity result in either your activity or in compose scope with:

// [Activity] Register with the contract first
val someOperationLauncher = registerForActivityResult(operationContract) { result ->
  // handle the activity result here
  when (warmUpResult) {
    is AppResponse.Failed -> {
        /* handle failed */
    }
    is AppResponse.Success -> {
        /* handle success */
    }
  }
}

// [Compose Scope] Remember the launcher to avoid redundant instantiation during re-composition
val someOperationLauncher =
  rememberLauncherForActivityResult(operationContract) { result ->
    // handle the activity result here
    when (warmUpResult) {
      is AppResponse.Failed -> { /* handle failed */
      }
      is AppResponse.Success -> { /* handle success */
      }
    }
  }

// in call site
fun triggerOperation() {
  // optional data required for specific operation like the transaction amount
  someOperationLauncher.launch(requiredData)
}

Warm Up

The WarmUp calls the app to perform a series of runtime checking. It takes no argument.

// activity
private val warmUpLauncher = registerForActivityResult(n2appLibApi.warmUpContract()) {
  // handle the activity result here
}

// compose scope
val warmUpLauncher = rememberLauncherForActivityResult(n2appLibApi.warmUpContract()) {
  // handle the activity result here
}

// call site
fun triggerWarmUp() {
  warmUpLauncher.launch()
}

Activation

The Activation calls the app to activate the app, loads the necessary configs, like store information and EMV configs for later transaction.

Required the activationCode

// activity
private val activationLauncher = registerForActivityResult(n2appLibApi.activationContract()) {
  // handle the activity result here
}

// compose scope
val activationLauncher = rememberLauncherForActivityResult(n2appLibApi.activationContract()) {
  // handle the activity result here
}

// call site
fun triggerActivation(code: String) {
  activationLauncher.launch(AppRequest.Activation(activationCode = code))
}

Transaction

Initiate a transaction request, including the SALE, VOID, or REFUND.

For new transaction SALE, requires amount field.

For VOID & REFUND request, requires the orgTranId.

// activity
private val transactionLauncher = registerForActivityResult(n2appLibApi.transactionContract()) {
  // handle the activity result here
}

// compose scope
val transactionLauncher = rememberLauncherForActivityResult(n2appLibApi.transactionContract()) {
  // handle the activity result here
}

// call site
// card sale request
fun triggerSaleTransaction(amount: BigDecimal, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.Sale(
      amount = amount,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId,
      // auto dismiss the SoftPOS transaction result screen and return to Business App
      autoDismissResult = true
    )
  )
}

// QR payment(Merchant Scan) request
fun triggerSaleTransaction(amount: BigDecimal, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.Sale(
      amount = amount,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId,
      // auto dismiss the SoftPOS transaction result screen and return to Business App
      autoDismissResult = true,
      // initiate QR transaction with merchant scan mode
      preferredInstrument = PreferredInstrument.QR_MERCHANT_SCAN
    )
  )
}

// QR payment(Guest Scan) request
fun triggerSaleTransaction(amount: BigDecimal, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.Sale(
      amount = amount,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId,
      // auto dismiss the SoftPOS transaction result screen and return to Business App
      autoDismissResult = true,
      // initiate QR transaction with guest scan mode
      preferredInstrument = PreferredInstrument.QR_GUEST_SCAN,
      // optional,in QR guest scan mode, if no QR payment method is specified, the app will display a list of supported QR payment methods for the user to select from
      qrPaymentMethods = QRPaymentMethods.ALIPAY_PLUS
    )
  )
}

// void request
fun triggerVoidTransaction(originalTransactionId: String, passcode: String, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.Void(
      orgTranId = originalTransactionId,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId,
      // merchant admin passcode for void/ refund
      adminPwd = passcode
    )
  )
}

// Pre-Auth request
fun triggerPreAuthTransaction(amount: BigDecimal, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.Auth(
      amount = amount,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId,
      // auto dismiss the SoftPOS transaction result screen and return to Business App
      autoDismissResult = true
    )
  )
}

// Auth completion(Capture) request
fun triggerAuthCompletionTransaction(originalTransactionId: String, amount: BigDecimal, posMessageId: String) {
  transactionLauncher.launch(
    AppRequest.Transaction.AuthCompletion(
      orgTranId = originalTransactionId,
      amount = amount,
      // posMessageId - a reference from POS system, that will pass to n2Tap backend for later query
      posMessageId = posMessageId
    )
  )
}

Enquiry Transaction Status with orignalTranId

To enquiry previous transaction by the transaction ID

// activity
private val enquiryTranLauncher = registerForActivityResult(n2appLibApi.enquiryTranStatusContract()) {
  // handle the activity result here
}

// compose scope
val enquiryTranLauncher = rememberLauncherForActivityResult(n2appLibApi.enquiryTranStatusContract()) {
  // handle the activity result here
}

// call site
fun triggerEnquiryTranStatus(orignalTranId: String) {
  enquiryTranLauncher.launch(AppRequest.EnquiryTranStatus(orgTranId = orignalTranId))
}

Enquiry Transaction Status with posMessageId

To enquiry previous transaction by the posMessageId

// activity
private val enquiryTranLauncher = registerForActivityResult(n2appLibApi.enquiryTranStatusWithMessageIdContract()) {
    // handle the activity result here
}

// compose scope
val enquiryTranLauncher = rememberLauncherForActivityResult(n2appLibApi.enquiryTranStatusWithMessageIdContract()) {
    // handle the activity result here
}

// call site
fun triggerEnquiryTranStatus(posMsgId: String) {
    enquiryTranLauncher.launch(AppRequest.EnquiryTranStatusWithMessageId(posMessageId = posMsgId))
}

Reload configuration

Force to reload EMV configurations and payment keys

// activity
private val reloadConfigurationLauncher = registerForActivityResult(n2appLibApi.reloadConfigurationContract()) {
  // handle the activity result here
}

// compose scope
val reloadConfigurationLauncher = rememberLauncherForActivityResult(n2appLibApi.reloadConfigurationContract()) {
  // handle the activity result here
}

// call site
fun reloadConfiguration() {
  reloadConfigurationLauncher.launch()
}

Enquiry Bluetooth Connection Status

Obtain the Bluetooth connection status between phone and MPOS D177 device

// activity
private val enquiryBTConnectStatusLauncher = registerForActivityResult(n2appLibApi.enquiryBTConnectStatusContract()) {
  // handle the activity result here
}

// compose scope
val enquiryBTConnectStatusLauncher = rememberLauncherForActivityResult(n2appLibApi.enquiryBTConnectStatusContract()) {
  // handle the activity result here
}

// call site
fun enquiryBTConnectStatus() {
  enquiryBTConnectStatusLauncher.launch()
}

Settlement

To settle all available batches:

// activity
private val settlementLauncher = registerForActivityResult(n2appLibApi.settlementContract()) {
  // handle the activity result here
}

// compose scope
val settlementLauncher = rememberLauncherForActivityResult(n2appLibApi.settlementContract()) {
  // handle the activity result here
}

// call site
fun triggerSettlement(posMessageId: String) {
  settlementLauncher.launch(AppRequest.Settlement(posMessageId = posMessageId))
}

Response Code & Data Model

Refer to Response Code for more details

Refer to Data Model for more details

On this page