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:
| Operation | Method | Route | Description |
|---|---|---|---|
Status | GET | /api/v1/status | Check the device status |
WarmUp | GET | /api/v1/warmup | Warm up MineSec Pay 7: sync configs and run the runtime checks on the device and app |
Activation | POST | /api/v1/activation | Activate the app and load configs from the backend |
Transaction | POST | /api/v1/trans | Start a transaction: sale, pre-auth, auth completion, incremental auth, void or refund |
Enquiry | GET | /api/v1/trans/{tranId} | Check the status of a previous transaction by its transaction ID |
Enquiry | GET | /api/v1/trans/status/{tranId} | Same as above. Also takes the transaction ID, not the posMessageId. |
Settlement | POST | /api/v1/settlement | Settle the current batch |
Cancel | GET | /api/v1/cancel | Cancel the operation in progress |
Reload | GET | /api/v1/reload | Reload 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 status | When |
|---|---|
200 | The request was handled. Check rspCode for the result, including declined transactions and amount validation errors. |
400 | Invalid request: unknown name, or a required field such as orgTranId, adminPwd or activationCode is missing. |
500 | The body is missing or is not valid JSON (rspCode -100). |
404 | Unknown 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:
name | Required fields | Optional fields |
|---|---|---|
SALE | amount | description, posMessageId, autoDismissResult, preferredInstrument, qrPaymentMethods |
AUTH (pre-auth) | amount | description, posMessageId, autoDismissResult |
AUTH_COMP (auth completion) | orgTranId, amount | posMessageId, adminPwd |
INCREMENTAL_AUTH | orgTranId, amount | posMessageId, adminPwd |
VOID | orgTranId | posMessageId, adminPwd (required when the terminal's supervisor policy asks for it) |
REFUND | orgTranId | adminPwd (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 byposMessageId, use App2App withEnquiryTranStatusWithMessageId. 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