MineSec Pay 7
API Reference
Quickstart (Local Network)

Integration via local network

MineSec Pay 7 runs a Local PayServer, a simple web server that accepts HTTP requests from the local network.

Pay Server Http Call

The PayServer provides endpoints for the following operations:

OperationMethodRouteDescription
StatusGET/api/v1/statusCheck the device status
WarmUpGET/api/v1/warmupWarm up MineSec Pay 7: sync configs and run the runtime checks on the device and app
ActivationPOST/api/v1/activationActivate the app and load configs from the backend
TransactionPOST/api/v1/transStart a transaction: sale, pre-auth, auth completion, incremental auth, void or refund
EnquiryGET/api/v1/trans/{tranId}Check the status of a previous transaction by its transaction ID
EnquiryGET/api/v1/trans/status/{tranId}Same as above. Also takes the transaction ID, not the posMessageId.
SettlementPOST/api/v1/settlementSettle the current batch
CancelGET/api/v1/cancelCancel the operation in progress
ReloadGET/api/v1/reloadReload the EMV configurations and payment keys

GET / returns a plain-text list of the supported endpoints, which is useful to check that the PayServer is reachable.

The PayServer listens on port 8000 by default. It needs no authentication and no headers other than Content-Type: application/json on POST requests.

Every route answers with a PosResponse (rspCode, rspMsg, and data / tranId where relevant). HTTP status codes:

HTTP statusWhen
200The request was handled. Check rspCode for the result, including declined transactions and amount validation errors.
400Invalid request: unknown name, or a required field such as orgTranId, adminPwd or activationCode is missing.
500The body is missing or is not valid JSON (rspCode -100).
404Unknown route.

Generally you'll want to create HTTP client to request server.

val client = HttpClient(Android) {
  install(ContentNegotiation) {
    gson {
      registerTypeAdapter(PosRequest::class.java, PosRequestDeserializer())
    }
  }
}
 
// default PayServer port: 8000
val result =
  client.post("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/trans") {
    contentType(ContentType.Application.Json)
    setBody(request)
  }
// Custom Deserializer
class PosRequestDeserializer : JsonDeserializer<PosRequest> {
  override fun deserialize(
    json: JsonElement,
    typeOfT: Type,
    context: JsonDeserializationContext
  ): PosRequest {
    val jsonObject = json.asJsonObject
    val name = jsonObject.get("name").asString
 
    return when (name) {
      "WARM_UP" -> PosRequest.WarmUp
      "ACTIVATION" -> context.deserialize<PosRequest.Activation>(
        jsonObject,
        PosRequest.Activation::class.java
      )
 
      "ENQUIRY_STATUS" -> PosRequest.EnquiryDeviceStatus
      "RELOAD_CONFIGS" -> PosRequest.ReloadConfiguration
      "SALE" -> context.deserialize<PosRequest.Transaction.Sale>(
        jsonObject,
        PosRequest.Transaction.Sale::class.java
      )
 
      "AUTH" -> context.deserialize<PosRequest.Transaction.Auth>(
        jsonObject,
        PosRequest.Transaction.Auth::class.java
      )
 
      "VOID" -> context.deserialize<PosRequest.Transaction.Void>(
        jsonObject,
        PosRequest.Transaction.Void::class.java
      )
 
      "REFUND" -> context.deserialize<PosRequest.Transaction.Refund>(
        jsonObject,
        PosRequest.Transaction.Refund::class.java
      )
 
      "AUTH_COMP" -> context.deserialize<PosRequest.Transaction.AuthCompletion>(
          jsonObject,
          PosRequest.Transaction.AuthCompletion::class.java
      )
 
      "INCREMENTAL_AUTH" -> context.deserialize<PosRequest.Transaction.IncrementalAuth>(
          jsonObject,
          PosRequest.Transaction.IncrementalAuth::class.java
      )
 
      "ENQUIRY_TRANSACTION_STATUS" -> context.deserialize<PosRequest.EnquiryTranStatus>(
        jsonObject,
        PosRequest.EnquiryTranStatus::class.java
      )
 
      "SETTLEMENT" -> context.deserialize<PosRequest.Settlement>(
        jsonObject,
        PosRequest.Settlement::class.java
      )
 
      else -> throw JsonParseException("Unknown type: $name")
    }
  }
}

Warm Up

The WarmUp calls MineSec Pay 7 to perform a series of runtime checking. It takes no argument.

 val result =
  client.get("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/warmup")
val response = result.body<PosResponse<Unit>>()

Activation

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

Required the activationCode

val request = PosRequest.Activation("activation code")
 
val result =
  client.post("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/activation") {
    contentType(ContentType.Application.Json)
    setBody(request)
  }
val response = result.body<PosResponse<Unit>>()

Transaction

Start a transaction. The name field selects the type:

nameRequired fieldsOptional fields
SALEamountdescription, posMessageId, autoDismissResult, preferredInstrument, qrPaymentMethods
AUTH (pre-auth)amountdescription, posMessageId, autoDismissResult
AUTH_COMP (auth completion)orgTranId, amountposMessageId, adminPwd
INCREMENTAL_AUTHorgTranId, amountposMessageId, adminPwd
VOIDorgTranIdposMessageId, adminPwd (required when the terminal's supervisor policy asks for it)
REFUNDorgTranIdadminPwd (required when the terminal's supervisor policy asks for it)

amount is a decimal amount in the terminal's currency, for example "10.50". The currency comes from the terminal's configuration, not from the request.

val cardSaleRequest = PosRequest.Transaction.Sale(
  amount = BigDecimal(amount),
  posMessageId = viewModel.posMessageId,
  autoDismissResult = true
)
 
val qrMerchantScanRequest = PosRequest.Transaction.Sale(
  amount = BigDecimal(amount),
  posMessageId = viewModel.posMessageId,
  autoDismissResult = true,
  // initiate QR transaction with merchant scan mode
  preferredInstrument = PreferredInstrument.QR_MERCHANT_SCAN
)
 
val qrGuestScanRequest = PosRequest.Transaction.Sale(
  amount = BigDecimal(amount),
  posMessageId = viewModel.posMessageId,
  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, MineSec Pay 7 will display a list of supported QR payment methods for the user to select from
  qrPaymentMethods = QRPaymentMethods.ALIPAY_PLUS
)
 
val voidRequest = PosRequest.Transaction.Void(
  orgTranId = "Original Trans ID",
  posMessageId = "xxxx",
  adminPwd = "yyyy"
)
 
val authRequest = PosRequest.Transaction.Auth(
    amount = viewModel.getAmountForSale(),
    posMessageId = viewModel.posMessageId,
    autoDismissResult = viewModel.autoDismissTransaction
)
 
val authCompRequest = PosRequest.Transaction.AuthCompletion(
    orgTranId = viewModel.cachedTransactionId,
    amount = viewModel.getAmountForSale(),
    posMessageId = viewModel.posMessageId
)
 
val incrementalAuthRequest = PosRequest.Transaction.IncrementalAuth(
    orgTranId = viewModel.cachedTransactionId,
    amount = viewModel.getIncrementAmount(),
    posMessageId = viewModel.posMessageId
)
 
val refundRequest = PosRequest.Transaction.Refund(
  orgTranId = "Original Trans ID",
  adminPwd = "yyyy"
)
 
val result =
  client.post("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/trans") {
    contentType(ContentType.Application.Json)
    setBody(request)
  }
val response = result.body<PosResponse<TransactionResponse>>()

Enquiry Transaction Status

Query a previous transaction by its transaction ID (tranId in the transaction response). Both routes do the same lookup:

val result = client.get("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/trans/${tranId}")
// or: /api/v1/trans/status/${tranId}
val response = result.body<PosResponse<TransactionResponse>>()

Note: the Local PayServer cannot query by posMessageId. The ID in the path is always matched against the transaction ID. To query by posMessageId, use App2App with EnquiryTranStatusWithMessageId. See Quickstart (App to App).

Device Status

Check that MineSec Pay 7 is running and ready.

val result = client.get("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/status")
val response = result.body<PosResponse<Unit>>()

Settlement

Settle the current batch. The body is optional.

val request = PosRequest.Settlement(posMessageId = viewModel.posMessageId)
 
val result =
  client.post("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/settlement") {
    contentType(ContentType.Application.Json)
    setBody(request)
  }
val response = result.body<PosResponse<SettlementResponse>>()

Cancel

Cancel the operation in progress, for example a sale waiting for the card.

val result = client.get("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/cancel")
val response = result.body<PosResponse<Unit>>()

Reload configuration

Force to reload EMV configurations and payment keys

val result = client.get("http://${remoteIpAddress.value}:${remotePort.value}/api/v1/reload")
val response = result.body<PosResponse<Unit>>()

Response Code & Data Model

Refer to Response code for more details

Refer to Data Model for more details