Integrating an app with POSfix
Your app runs on the same Android terminal as POSfix and sends it commands: a sale, a card payment, a Z report. POSfix does the fiscal and banking part, then gives you back the result. Its screen does not open.
This page is for the developers who write the app. What you send in each command is in the Command reference.
What your app can do
- Fiscal sale, paid in cash, by card or with MIA QR. POSfix sends the receipt to SFS and prints it.
- Cash in and cash out, with a service receipt.
- X report and Z report. The Z report also closes the banking day of the card terminal.
- Void and refund of a card payment.
- Receipt copy, the list of sales, the fiscal day summary.
- The catalog: products, categories, VAT rates, cashiers.
- The loyalty program customer: you look them up by phone or card, and the sale adds points to their balance.
What it does not do
- It does not issue a return receipt. The SFS protocol (ACPS) has only the fiscal receipt, the service receipt and the report. You give the money back with
card.voidorcard.refund, and the return of the goods stays outside the integration. - It does not pay with the loyalty bonus and does not apply rewards. For now it only identifies the customer and adds their points.
- It does not activate the terminal. You activate it once, on the POSfix screen.
The only step on the terminal: activation
You install POSfix and activate it once, from its screen, with the code generated in Control Panel when the terminal is assigned. After that there is nothing to configure: no key, no approval, no cashier logged in.
Activation is also the license. As long as the terminal is activated, any app on it can send commands. If you deactivate it from Control Panel, POSfix refuses commands with TERMINAL_NOT_ACTIVATED starting with its next connection to the server.
How the two apps work together
- Your app binds to the POSfix service. If POSfix was not running, Android starts it, without a screen.
- It sends the command as JSON: the command name, its data and a unique key.
- POSfix runs it with the same code as the POS screen: the receipt comes out identical to one issued by a cashier.
- The result comes back on a callback, exactly once.
The connection goes through Android (Binder), not through the network. Android tells POSfix which app sends the command, and that app's package shows up in the terminal's request log.
After the first command, POSfix keeps running in the background. It starts on its own after a terminal restart and after an update, and “POSfix” appears in the notification bar.
Quick start
1. Add the SDK. In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://docs.posfix.md/sdk/maven") }
}
}
In your app's build.gradle.kts:
dependencies {
implementation("md.posfix:posfix-integration-sdk:1.0.0")
}
The SDK needs Android 7.0 (API 24) or newer. It adds to your manifest, on its own, the <queries> declaration for md.posfix.posfix that Android 11+ requires, so you don't write anything extra.
2. Connect.
val posfix = PosfixClient(context)
posfix.connect(object : PosfixClient.ConnectionListener {
override fun onConnected(apiVersion: Int) {
// ready for commands
}
override fun onDisconnected(reason: String) {
// POSfix stopped; commands in progress get POSFIX_DISCONNECTED,
// and the SDK binds again on its own
}
})
3. Check the terminal.
posfix.execute("status") { response ->
val data = response.getJSONObject("data")
data.getBoolean("activated") // false = activate POSfix on its screen
}
4. Issue your first receipt. A 25 lei coffee, paid in cash:
val sale = JSONObject("""
{
"items": [ { "name": "Cafea", "unitPrice": 25.00, "quantity": 1, "vatCode": "B" } ],
"payments": [ { "type": "1", "amount": 25.00 } ],
"amountReceived": 25.00,
"printReceipt": true
}
""")
val key = PosfixClient.newIdempotencyKey() // keep it until you know the result
val sent = posfix.execute("sale.create", sale, key) { response ->
if (response.getBoolean("success")) {
val receipt = response.getJSONObject("data").getInt("receiptNumber")
} else {
val code = response.getJSONObject("error").getString("code")
}
}
All callbacks arrive on the main thread. execute returns right away with sent.requestId, which you need to stop the sale with cancel.
For a card payment you set "type": "2", and POSfix opens the bank's payment screen. The details are in Card payment, events and failures.
Flutter, React Native and the test app
The SDK also works from the Android side of a Flutter or React Native app. The direct AIDL interface is for those who don't want the SDK dependency: see “Without the SDK” in the Command reference.
The SDK source code comes with the Maven package. You get the demo app, with buttons for all the commands, on request, at info@m-373.com.