Android SDK
Overview
The goal of the BluePOS Go SDK library is to provide an interface for communication between external third-party applications and the BluePOS Go application.
The SDK exposes:
- An AIDL interface
- Data classes required to send requests and receive responses
Requirements
- The BluePOS Go application must be installed on the device
- The BluePOS Go application must have the AIDL service implemented
Sample Application
This guide walks you through setting up the BluefinPOSGoSDK for Android, step by step. For a complete example of how the SDK components work together, see our sample application on GitHub.
Adding the Library
Add the BluePOS SDK .aar file to your project (e.g., in the libs/ folder).
Then include it in your build.gradle:
dependencies {
implementation(files("libs/blueposgo-sdk-release.aar"))
}SDK Imports
Once the SDK library is added to your project, here is the comprehensive list of what you need to import based on your needs.
import android.content.Intent
import android.util.Log
import com.bluefin.blueposgo.sdk.ACTION_EXTERNAL
import com.bluefin.blueposgo.sdk.BLUEPOS_GO_ACTIVITY
import com.bluefin.blueposgo.sdk.BLUEPOS_GO_PACKAGE
import com.bluefin.blueposgo.sdk.EXTRA_CAPTURE_REQUEST
import com.bluefin.blueposgo.sdk.EXTRA_CLEAR_DATA_REQUEST
import com.bluefin.blueposgo.sdk.EXTRA_COMMAND
import com.bluefin.blueposgo.sdk.EXTRA_CONNECT
import com.bluefin.blueposgo.sdk.EXTRA_DISCONNECT
import com.bluefin.blueposgo.sdk.EXTRA_FORGET
import com.bluefin.blueposgo.sdk.EXTRA_FULL_REFUND_REQUEST
import com.bluefin.blueposgo.sdk.EXTRA_INIT
import com.bluefin.blueposgo.sdk.EXTRA_PAYLOAD
import com.bluefin.blueposgo.sdk.EXTRA_PAYMENT_REQUEST
import com.bluefin.blueposgo.sdk.EXTRA_REBOOT
import com.bluefin.blueposgo.sdk.EXTRA_REFUND_REQUEST
import com.bluefin.blueposgo.sdk.EXTRA_TR_LIST_REQUEST
import com.bluefin.blueposgo.sdk.PAYMENT_SERVICE_ACTION
import com.bluefin.blueposgo.sdk.PaymentCallback
import com.bluefin.blueposgo.sdk.PaymentServiceAIDL
import com.bluefin.blueposgo.sdk.request.ClearDataRequest
import com.bluefin.blueposgo.sdk.request.FullRefundRequest
import com.bluefin.blueposgo.sdk.request.InitRequest
import com.bluefin.blueposgo.sdk.request.PaymentRequest
import com.bluefin.blueposgo.sdk.request.PostProcessRequest
import com.bluefin.blueposgo.sdk.response.ClearDataResponse
import com.bluefin.blueposgo.sdk.response.DeviceCommandResponse
import com.bluefin.blueposgo.sdk.response.InitResponse
import com.bluefin.blueposgo.sdk.response.PaymentResponse
import com.bluefin.testaidlgo.data.REFUND
import com.bluefin.testaidlgo.data.createCaptureRequest
import com.bluefin.testaidlgo.data.createClearDataRequest
import com.bluefin.testaidlgo.data.createFullRefundRequest
import com.bluefin.testaidlgo.data.createInitRequest
import com.bluefin.testaidlgo.data.createPaymentRequest
import com.bluefin.testaidlgo.data.createRefundRequest
import com.bluefin.testaidlgo.ui.MainScreen
import com.bluefin.testaidlgo.ui.events.MainScreenEvents
import com.bluefin.testaidlgo.ui.theme.TestAIDLThemeUsage
NoteNote that all of the following examples should be put together within a single
MainActivityclass.
To communicate with BluePOS Go, bind to its AIDL service from your Activity.
In other words, the entire communication with BluePOS Go revolves around the MainActivity that binds to the AIDL service.
Essential MainActivity Setup
This activity is created to send a sample payment to BluePOS and get the result.
class MainActivity : ComponentActivity() {
private var service: PaymentServiceAIDL? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
service = PaymentServiceAIDL.Stub.asInterface(binder)
}
override fun onServiceDisconnected(name: ComponentName?) {
service = null
}
}
override fun onStart() {
super.onStart()
val intent = Intent(PAYMENT_SERVICE_ACTION)
intent.setPackage(BLUEPOS_GO_PACKAGE)
bindService(intent, connection, BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
unbindService(connection)
service = null
}
}After onServiceConnected , the onStart must be performed via Intent to bind the service.
BluePOS Go's payment work lives in PaymentServiceAIDL. Do not rely on that service to open BLUEPOS_GO_ACTIVITY itself.
Background activity starts are restricted according to app visibility, OS version, target SDK level, and any applicable exception. Those restrictions are not a single Android 14 rule. Use the supplied explicit activity handoff from a merchant screen that is already visible, and verify that handoff on the OS and target SDK combination you intend to ship.
That is why startThroughService does both steps.
paymentCallback
The next step is to set up the paymentCallback that receives the responses from BluePOS Go App. This is a set of callbacks that ensure the responses from the established PaymentServiceAIDL connection.
class MainActivity : ComponentActivity() {
private var service: PaymentServiceAIDL? = null
private var statusText by mutableStateOf("Ready")
private var debugMode by mutableStateOf(true)
private var transList by mutableStateOf(listOf<PaymentResponse?>())
private var refundsList by mutableStateOf(listOf<PaymentResponse>())
private val paymentCallback = object : PaymentCallback.Stub() {
override fun onPaymentResult(response: PaymentResponse) {
runOnUiThread {
Log.d("Receive result", response.toString())
statusText = formatPaymentResponse(response)
}
}
override fun onClearDataResult(response: ClearDataResponse?) {
runOnUiThread {
Log.d("Receive result", response.toString())
statusText = response?.let { formatClearDataResponse(it) } ?: "Unknown result"
}
}
@Suppress("UNCHECKED_CAST")
override fun onTransactionsListResponse(list: List<PaymentResponse?>?) {
statusText = "List received"
list?.let { lst ->
transList = lst
refundsList = lst.filter { it?.type == REFUND } as List<PaymentResponse>
}
}
override fun onInitResult(response: InitResponse?) {
runOnUiThread {
statusText = response?.status ?: ""
}
}
override fun onRebootResult(response: InitResponse?) {
statusText = response?.status ?: ""
}
override fun onError(message: String?) {
runOnUiThread {
statusText = "Payment error: $message"
}
}
override fun onDeviceCommandResult(response: DeviceCommandResponse?) {
runOnUiThread {
statusText = "Device command ${response?.command} result: ${response?.status}"
}
}
}
}startThroughService and startActivity
startThroughService and startActivitystartThroughService sends a payment action to BluePOS Go and then opens the BluePOS Go screen by starting the activity via openBluePosGoPayment.
It checks that the service is connected. It sends the request, such as a payment, refund, or init, to the BluePOS Go service. If the service accepts it, the function opens the BluePOS Go app so the merchant can finish the action on the reader. The result comes back later through the callback.
If the service is missing, busy, or not initialized, it stops and shows an error instead of opening the app.
class MainActivity : ComponentActivity() {
// ...
private fun startThroughService(
command: String,
request: Any? = null
) {
if (service == null) {
statusText = UNKNOWN_COMMAND
return
}
try {
var serviceResult: Boolean? = false
service?.debugMode = debugMode
if (service?.isInitialized != true && command != EXTRA_INIT) {
statusText = getString(R.string.not_initialized)
return
}
when (command) {
EXTRA_PAYMENT_REQUEST -> request?.let {
serviceResult = service?.payment(request as PaymentRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_CLEAR_DATA_REQUEST -> request?.let {
serviceResult = service?.clearDataRead(request as ClearDataRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_FULL_REFUND_REQUEST -> request?.let {
serviceResult = service?.fullRefund(request as FullRefundRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_REFUND_REQUEST -> request?.let {
serviceResult = service?.refund(request as PostProcessRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_CAPTURE_REQUEST -> request?.let {
serviceResult = service?.capture(request as PostProcessRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_INIT -> request?.let {
serviceResult = service?.init(request as InitRequest, paymentCallback)
} ?: { statusText = UNKNOWN_COMMAND }
EXTRA_TR_LIST_REQUEST ->
serviceResult = service?.getTransactionsListResponse(paymentCallback)
EXTRA_REBOOT -> serviceResult = service?.reboot(paymentCallback)
EXTRA_CONNECT -> serviceResult = service?.connectDevice(paymentCallback)
EXTRA_DISCONNECT -> serviceResult = service?.disconnectDevice(paymentCallback)
EXTRA_FORGET -> serviceResult = service?.forgetDevice(paymentCallback)
else -> statusText = UNKNOWN_COMMAND
}
if (serviceResult == true) {
openBluePosGoPayment(command, request?.let { it as Parcelable })
statusText = "Payment started. Waiting for callback..."
} else
statusText = "Service is busy or not started"
} catch (e: RemoteException) {
statusText = "PaymentService error: ${e.message}"
} catch (_: ActivityNotFoundException) {
statusText = "BluePOS Go activity was not found"
}
}
private fun openBluePosGoPayment(
command: String,
request: Parcelable?
) {
val intent = Intent(ACTION_EXTERNAL)
.setClassName(BLUEPOS_GO_PACKAGE, BLUEPOS_GO_ACTIVITY)
.putExtra(EXTRA_COMMAND, command)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
request?.let {
intent.putExtra(EXTRA_PAYLOAD, request)
}
startActivity(intent)
}
}Initialize BluePOS Go
Initialize the BluePOS Go application before attempting a payment.
val request = createInitRequest()
startThroughService(EXTRA_INIT, request)Making Payment
private fun processAction(
event: MainScreenEvents,
updateStatus: (String) -> Unit,
updateChecked: (Boolean) -> Unit
) {
// ...
val request = createPaymentRequest(event.amount)
startThroughService(EXTRA_PAYMENT_REQUEST, request)
// ...
}How It Works
- Bind to the BluePOS AIDL service using an explicit intent.
- Obtain an instance of
PaymentServiceAIDL. - Call service methods to:
- Process payments
- Perform refunds and authorizations
- Retrieve transactions
- Create intent and call activity
- Handle result in callback, passed to service
API Authentication and Building BasicToken
The sample below is taken from the sample application. It shows how to authenticate with the API before you build a payment request.
To build your basic token for your SDK instance, please create your respective API key under your PayConex account ID and use the following code to generate the basic authentication. For more details, refer to API Authentication | Basic Authentication.
The example below also demonstrates all the payment helpers you will need in practice in order to process sale, auth, refunds, captures, and other operations.
Scope DefinitionsDepending on the operation you are running on your device, make sure to add the corresponding scope permissions to your API key being used.
For all the scope permissions, please refer to API Key Management | Scope Definitions.
import android.util.Base64
import com.bluefin.blueposgo.sdk.request.ClearDataRequest
import com.bluefin.blueposgo.sdk.request.FullRefundRequest
import com.bluefin.blueposgo.sdk.request.InitRequest
import com.bluefin.blueposgo.sdk.request.PaymentRequest
import com.bluefin.blueposgo.sdk.request.PostProcessRequest
import com.bluefin.blueposgo.sdk.request.PostProcessType
import com.bluefin.testaidlgo.BuildConfig
import java.math.RoundingMode
const val REFUND = "refund"
const val AUTHORIZED = "AUTHORIZED"
private data class PaymentConfig(
val accountId: String,
val apiKey: String,
val apiSecret: String,
val environment: String
)
private fun requirePaymentConfig(): PaymentConfig {
val config = PaymentConfig(
accountId = BuildConfig.BLUEPOS_ACCOUNT_ID,
apiKey = BuildConfig.BLUEPOS_API_KEY,
apiSecret = BuildConfig.BLUEPOS_API_SECRET,
environment = BuildConfig.BLUEPOS_ENVIRONMENT
)
val missing = listOf(
"BLUEPOS_ACCOUNT_ID" to config.accountId,
"BLUEPOS_API_KEY" to config.apiKey,
"BLUEPOS_API_SECRET" to config.apiSecret,
"BLUEPOS_ENVIRONMENT" to config.environment
).filter { (_, value) -> value.isBlank() }
.joinToString { (name, _) -> name }
check(missing.isEmpty()) {
"Missing payment configuration: $missing. Add values to payment.properties, Gradle properties, or environment variables."
}
return config
}
private fun buildBasicToken(apiKey: String, apiSecret: String): String {
val credentials = "$apiKey:$apiSecret"
val encoded = Base64.encodeToString(
credentials.toByteArray(Charsets.UTF_8),
Base64.NO_WRAP
)
return "Basic $encoded"
}
fun createPaymentRequest(amount: Long, tip: Long): PaymentRequest {
val config = requirePaymentConfig()
return PaymentRequest(
amount = amount.toDouble() / 100,
tip = tip.toDouble() / 100,
type = "sale",
notes = "External application", // not really needed
accountId = config.accountId,
basicToken = buildBasicToken(config.apiKey, config.apiSecret),
environment = config.environment
)
}
fun createRefundRequest(transactionId: String, amount: Long): PostProcessRequest {
return PostProcessRequest(
amount = amount.toDouble() / 100,
transactionId = transactionId,
type = PostProcessType.REFUND
)
}
fun createCaptureRequest(transactionId: String, amount: Long): PostProcessRequest {
return PostProcessRequest(
amount = amount.toDouble() / 100,
transactionId = transactionId,
type = PostProcessType.CAPTURE
)
}
fun createClearDataRequest(amount: Long): ClearDataRequest {
return ClearDataRequest(
amount = amount.toDouble() / 100,
timeOut = 60
)
}
fun createFullRefundRequest(id: String): FullRefundRequest {
return FullRefundRequest(
transactionId = id
)
}
fun createInitRequest(): InitRequest {
val config = requirePaymentConfig()
return InitRequest(
accountId = config.accountId,
basicToken = buildBasicToken(config.apiKey, config.apiSecret),
environment = config.environment
)
}
fun prepareAmountNumber(amountText: String): Long = amountText
.toBigDecimalOrNull()
?.setScale(2, RoundingMode.DOWN)
?.movePointRight(2)
?.toLong() ?: 0All of the device operations are based on the EXTRA_* enumerations based on the operation.
Initialize BluePOS Go
Initialize before attempting a payment.
val request = createInitRequest()
startThroughService(EXTRA_INIT, request)Sale With Tip
val request = createPaymentRequest(event.amount, event.tip)
startThroughService(EXTRA_PAYMENT_REQUEST, request)Authorization
val request = createPaymentRequest(event.amount, event.tip)
request.type = "auth"
startThroughService(EXTRA_PAYMENT_REQUEST, request)Save Card
val request = createPaymentRequest(0, 0)
request.type = "save"
startThroughService(EXTRA_PAYMENT_REQUEST, request)Clear Data
val request = createClearDataRequest(event.amount)
startThroughService(EXTRA_CLEAR_DATA_REQUEST, request)Capture Transaction
val request = createCaptureRequest(event.transactionId, event.amount)
startThroughService(EXTRA_CAPTURE_REQUEST, request)Partial Refund
val request = createRefundRequest(event.transactionId, event.amount)
startThroughService(EXTRA_REFUND_REQUEST, request)Full Refund
val request = createFullRefundRequest(event.id)
startThroughService(EXTRA_FULL_REFUND_REQUEST, request)Updated about 1 hour ago
