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 and running operations for Android, step by step. For a complete example of how the SDK components work together, see our Sample Applications and 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-x.x.x.aar"))
}Where the x.x.x represents the current version in use.
Package SupportTo get the latest BluePosGoSDK package for your iOS, please reach out to Bluefin Integrations team via [email protected].
Android Kotlin Interface API Reference
For the comprehensive reference on all enumeration values, statues and object types, function methods, refer to BluePosGoSDK Android API Reference. This includes all the transaction types, and parameters for all payment request and response objects, and other operations.
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 our 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.
Early AccessNote that the way the merchant is authenticating to the SDK will change in the future with only having to provide
apiKeyandapiSecretas the input credentials. The following is now strictly the case as the SDK is still in early (beta) access.
The example below also demonstrates all the payment helpers you will need in practice in order to process save, 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.
We recommend utilizing an API key with the following scope definitions:
"scopes": [ "pcx:poi:*", "pcx:payments:*", "pcx:payments:device:*", "pcx:payments:device:credit" ]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)Operation in Progress
To check if there are any operations already in progress on Android SDK, use the following direct service-state method:
val any_on_going_operations = service?.hasOngoingOperations()For the rest of the direct service-state methods, refer to BluePosGoSDK Android API Reference | Direct service-state methods.
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)Reboot
startThroughService(EXTRA_REBOOT, null)Forget Reader
startThroughService(EXTRA_FORGET, null)Updated 15 days ago
