MPoC SDK QuickStart
You can find the example project here via github (opens in a new tab)
Add the MineSec's registry
To get the MineSec's package, setup the credential and registry in your project:
# minesec client registry
MINESEC_REGISTRY_LOGIN=minesec-product-support
MINESEC_REGISTRY_TOKEN={token-value}
Please request to our customer support for the token.
Then in your project's settings gradle, setup the MineSec's registry as the following:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// MineSec's maven registry
maven {
val MINESEC_REGISTRY_LOGIN: String? by settings
val MINESEC_REGISTRY_TOKEN: String? by settings
requireNotNull(MINESEC_REGISTRY_LOGIN) {
"""
Please set your MineSec 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(MINESEC_REGISTRY_TOKEN)
println("MS GPR: $MINESEC_REGISTRY_LOGIN")
name = "MineSecMavenClientRegistry"
url = uri("https://maven.pkg.github.com/theminesec/ms-registry-client")
credentials {
username = MINESEC_REGISTRY_LOGIN
password = MINESEC_REGISTRY_TOKEN
}
}
}
}
Add dependency in app gradle
In your app's build.gradle.kts
, add the following dependency of the headless sdk:
dependencies {
// ... other deps
// ++
releaseImplementation("com.theminesec.sdk.mpoc:core-mpoc:2.01.001.010.025")
// ++ for debug SDK, you can declare as debugImplementation
debugImplementation("com.theminesec.sdk.mpoc:core-mpoc-stage:2.01.001.010.025")
}
Note that there are 2 versions:
core-mpoc
: it is connected to prod environment, with all security feature enabled.core-mpoc-stage
: it is for developing and debugging, note some checking might be loosen with this variant.
Exclude some resource file
In the app's build.gradle.kts
, also make sure the following packaging option exist (this is by default included in a new android project):
android {
// ...
packaging {
jniLibs.keepDebugSymbols.add("**/*.so")
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/META-INF/DEPENDENCIES"
excludes += "/META-INF/LICENSE"
excludes += "/META-INF/LICENSE.txt"
excludes += "/META-INF/license.txt"
excludes += "/META-INF/NOTICE"
excludes += "/META-INF/NOTICE.txt"
excludes += "/META-INF/notice.txt"
excludes += "/META-INF/ASL2.0"
excludes += "/META-INF/*.kotlin_module"
}
}
//...
configurations {
all {
exclude("bcprov-jdk15on")
exclude("bcpkix-jdk15on")
exclude("jetified-bcprov-jdk15on-1.56")
exclude("jetified-bcpkix-jdk15on-1.56")
exclude("bcprov-jdk18on")
exclude("bcpkix-jdk18on")
exclude("jetified-bcprov-jdk18on-1.77")
exclude("jetified-bcpkix-jdk18on-1.77")
exclude("META-INF/versions/9")
}
}
}
Put the .license
file to your project
Place the your-license-file.license
in the app module's ./src/main/assets
respectively
Please request to our customer support for the license if you haven't got one.
- your-license-file.license
Initialize SDK
In your application, you should initialize the MPoC SDK before doing any card reading operations.:
fun initSdk() = viewModelScope.launch {
// start initialization process
MPoCAPI.initSdk(licenseName, app) { result ->
when (result) {
is MPoCResult.Success -> {
writeMessage("init result success")
val sdkInitResult = result.data
}
is MPoCResult.Failure -> {
writeMessage("init fails " + result.code)
writeMessage("init fails message " + result.message)
writeMessage("init fail contextual " + result.contextual)
}
}
}
}
/**
below is the definition of sdkInitResult
public final data class SdkInitResult(
isRegistered: Boolean,
sdkId: String,
sdkBuildModel: String,
sdkVersion: String,
licenseId: String,
customerId: String,
serverUrl: String,
kekCert: String,
signCert: String
)
*/
Request a transaction
To make a new sale request is fairly straight forward:
From your other activity or fragment wish to launch the request, create a activity launcher for the result:
launcher = registerForActivityResult(
MineSecPaymentActivity.contract(MineSecPaymentActivity::class.java)
) {
when (it) {
is MPoCResult.Success -> {
Log.d("${it.javaClass.simpleName} \n" + gson.toJson(it))
}
is MPoCResult.Failure -> {
Log.d("transaction fails ${it.code} ${it.message} contextu=${it.contextual}")
}
}
}
The above launcher would print out whatever the result got back from the MineSecPaymentActivity
.
Then let's create another function to launch the Sale request:
fun launchSale() {
val validAmount = 1000 // Your input amount, it should be a "long" type
if (validAmount == null || validAmount <= 0) {
Log.d("Invalid amount. Transaction cannot proceed.")
return
}
val transactionDto = MhdEmvTransactionDto(
txnAmount = validAmount,
txnCurrencyText = "USD",
timeout = 80
)
// with customized PIN entry UI
// NOTE: this is optional, you can use MineSec default PIN Pad style
val pinPadConfig = PinPadConfig(
pinPadStyle = PinPadStyle(
backgroundColor = Color.LightGray.toArgb(),
amountStyle = getOswaldBold30(this@MainActivity),
digitStyle = getOswaldBold30(this@MainActivity),
descriptionStyle = getOswaldBold30(this@MainActivity),
buttonsColor = ButtonsColor(
abortBtn = Color.DarkGray.toArgb(),
confirmBtn = Color.Blue.toArgb(),
clearBtn = Color.Red.toArgb(),
)
),
// Choose the PIN PAD entry mode
pinEntryMode = PinEntryMode.SHUFFLE,
pinEntryDetails = PinEntryDetails(
// This message will be display on PIN Activity if PIN is required
pinDescription = "Your PIN Please!",
// this will be displayed on PIN PAD
amount = "100.75 USD",
// this is an additional message displayed under amount in PIN PAD
description = "Please pay MineSec monthly invoice",
// enable/disable PINBypass
supportPINBypass = false
)
)
val transactionDtoWithCustomizedPinPadUI = MhdEmvTransactionDto(
txnAmount = sdkViewModel.amount.toLong(),
txnCurrencyText = "USD",
pinpadConfig = gson.toJson(pinPadConfig)
)
// launcher.launch(transactionDtoWithCustomizedPinPadUI)
launcher.launch(transactionDto)
}
Note PIN PAD style is an optional configuration allows you to customize the PIN PAD display settings.
Wire it up
Now to wire it all up, let's consider this:
- When the app starts, in the
application
it'll do the init SDK, attestation will be performed in the background. - Then, launch a sale from the main activity, this request will be passed to the
MineSecPaymentActivity
import kotlinx.coroutines.flow.first
class MainActivity : AppCompatActivity() {
private val launcher = registerForActivityResult(
MineSecPaymentActivity.contract(MineSecPaymentActivity::class.java)
) {
when (it) {
is MPoCResult.Success -> {
Log.d("${it.javaClass.simpleName} \n" + gson.toJson(it))
}
is MPoCResult.Failure -> {
Log.d("transaction fails ${it.code} ${it.message} contextu=${it.contextual}")
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launcher = registerForActivityResult(
MineSecPaymentActivity.contract(MineSecPaymentActivity::class.java)
) {
when (it) {
is MPoCResult.Success -> {
sdkViewModel.writeMessage("${it.javaClass.simpleName} \n" + gson.toJson(it))
}
is MPoCResult.Failure -> {
sdkViewModel.writeMessage("transaction fails ${it.code} ${it.message} contextu=${it.contextual}")
}
}
}
setContent {
//.....
}
lifecycleScope.launch {
val result = registerSdk()
if (result.isRegistered) {
sdkViewModel.amount = "10000"
payment()
}
}
}
private val licenseName = "your_licens_file.license"
@SuppressLint("LogNotTimber")
suspend fun registerSdk() = suspendCoroutine { continuation ->
MPoCAPI.initSdk(licenseName,this.application) {
when (it) {
is MPoCResult.Failure -> {
Log.d(
"TAG",
"sdk registration fails ${it.code} ${it.message} context=${it.contextual}"
)
continuation.resumeWith(Result.failure<SdkInitResult>(Exception(it.contextual)))
}
is MPoCResult.Success -> {
Log.d("TAG", "register success $it")
continuation.resumeWith(Result.success<SdkInitResult>(it.data))
}
}
}
}
private fun payment() {
val validAmount = sdkViewModel.amount.toLongOrNull()
if (validAmount == null || validAmount <= 0) {
sdkViewModel.writeMessage("Invalid amount. Transaction cannot proceed.")
return
}
val transactionDto = MhdEmvTransactionDto(
txnAmount = sdkViewModel.amount.toLong(),
txnCurrencyText = "USD",
timeout = 80
)
// with customized PIN entry UI
val pinPadConfig = PinPadConfig(
pinPadStyle = PinPadStyle(
backgroundColor = Color.LightGray.toArgb(),
amountStyle = getOswaldBold30(this@MainActivity),
digitStyle = getOswaldBold30(this@MainActivity),
descriptionStyle = getOswaldBold30(this@MainActivity),
buttonsColor = ButtonsColor(
abortBtn = Color.DarkGray.toArgb(),
confirmBtn = Color.Blue.toArgb(),
clearBtn = Color.Red.toArgb(),
)
),
pinEntryMode = PinEntryMode.SHUFFLE,
pinEntryDetails = PinEntryDetails(
pinDescription = "Your PIN Please!",
amount = "100.75 USD",
description = "Please pay MineSec monthly invoice",
supportPINBypass = false
)
)
val transactionDtoWithCustomizedPinPadUI = MhdEmvTransactionDto(
txnAmount = sdkViewModel.amount.toLong(),
txnCurrencyText = "USD",
pinpadConfig = gson.toJson(pinPadConfig)
)
// launcher.launch(transactionDtoWithCustomizedPinPadUI)
launcher.launch(transactionDto)
}
}
Note: You MUST call initSdk() before any operation, otherwise it will fail
Run the app and you should be able to see:
