A Micro ATM lets a retailer serve a customer who has a debit card but no nearby ATM. The customer swipes or inserts their card into a small Bluetooth reader paired with the retailer's phone, enters their PIN on the reader, and gets cash from the retailer's drawer. Behind that simple counter flow sit three parties: your server, your Android app with the SDK inside it, and the bank network. Getting the hand-offs right is what keeps money safe when a phone dies mid-transaction. This guide follows the Payonclick Micro ATM API and its Android SDK reference.
How the pieces fit
| Part | What it does | What it must never do |
|---|---|---|
| Your server | Creates each transaction with a signed API call, receives the result report and the webhook | Expose your API key to the phone |
| Your Android app | Launches the SDK with the values from your server, saves the reference, sends the result to your server | Decide the final status on its own |
| The Micro ATM SDK | Talks to the Bluetooth reader, reads the card and PIN, returns an outcome | Pass card or PIN data to your app |
| The bank network | Approves or declines and confirms the transaction |
The key rule: the SDK result is evidence, not the final word. A withdrawal becomes SUCCESS, and your AEPS wallet is credited net of charges, only when the bank network's confirmation reaches Payonclick.
Before you start
- Outlet onboarding. Each outlet is onboarded and completes eKYC once through
/ext/v1/aeps/merchants, the same endpoints the AEPS API uses. - API key. A key with the
matmpermission, called only from your whitelisted server. - SDK files. Our team shares the SDK (
payonclick-microatm-sdk-v1.0.0.aar) and the card-reader driver (payonclick-card-reader-v1.0.0.jar). Copy both intoapp/libs. - Android 7.0 or later (API 24).
- A supported reader. PAX, MoreFun or NewLand Bluetooth readers.
Step 1: add the SDK to your app
Add a flatDir { dirs 'libs' } repository in app/build.gradle, include both files, and add the libraries the SDK depends on, listed in the reference. The SDK ships its own R8 and ProGuard rules, so a minified release build needs no extra rules.
Declare the Bluetooth and location permissions in the manifest: BLUETOOTH, BLUETOOTH_ADMIN, BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. Ask for the runtime permissions before you open the SDK. A denied Bluetooth permission is the most common reason a reader is "not found".
Step 2: create the transaction on your server
Your server calls POST /ext/v1/matm/transactions, signed with your API key, with the transaction type, the amount, the reader type and your own client_reference. The response carries a reference_id and an sdk object with the launch values.
Pass data.sdk and data.reference_id to the app. Never create the transaction from the phone: the request signature needs your secret, and a phone is not a safe place for it.
Use one client_reference per customer request, such as MATM-<outlet>-<your order id>. If the create call times out, read the transaction back by that reference instead of creating a second one.
Step 3: launch the SDK
In the app, build the intent with PayonclickMatm.createIntent(context, sdk, deviceId) and start it. Two details matter:
- Save the
reference_idfirst. Android can kill your app while the customer enters their PIN. Keep the reference inonSaveInstanceStateor on disk before you launch, so you can still report the result when the app returns. - Don't change the transaction type.
sdk.TYPEcomes from your server. It is cash withdrawal, balance enquiry or mini statement.
Step 4: read and report the result
When the SDK returns, parse it with PayonclickMatm.parseResult(resultCode, data). The result has an outcome, the amount, a message and, for a mini statement, its rows.
| Outcome | What the retailer does |
|---|---|
SUCCESS |
Hand over cash, or show the balance or statement |
FAILED |
No cash; show the message |
PENDING |
No cash yet; the bank has not decided |
CANCELLED or NO_RESPONSE |
Treat as pending; the bank may still complete it |
Send result.toReportJson() to your server, and have your server call POST /ext/v1/matm/transaction/{reference_id}/result. Always report, including cancelled and no-response outcomes, and queue the report on the phone until your server confirms it received it.
Step 5: wait for the final status
The final answer comes from the bank network. Subscribe to the matm.success and matm.failed webhooks, or poll GET /ext/v1/matm/transaction/{reference_id}. Your AEPS wallet is credited only on success.
Never start a new withdrawal for the same customer while one is pending. If the first one completes, the customer's account is debited twice. Wait for the final status, and show the retailer that the transaction is still being confirmed.
Common integration mistakes
- Calling the create endpoint from the app. It leaks your API key. Keep every signed call on your server.
- Losing the reference on process death. Save it before launching the SDK.
- Treating
CANCELLEDas failed. The bank may still have debited the card. Report it and wait. - Paying out on the SDK's word alone in your own ledger. Credit retailers only on the confirmed webhook or status.
- Skipping runtime permissions on Android 12 and later.
BLUETOOTH_SCANandBLUETOOTH_CONNECTare runtime permissions there.