Seamless payment processing between ERPNext / POS Awesome and GEIDEA credit card terminals via MQTT.
Overview
This project bridges ERPNext (and its POS Awesome module) with GEIDEA credit card machines. When a sale is made, the transaction amount is pushed to the physical credit card terminal over MQTT. The terminal processes the payment and sends the result back directly to the ERPNext server via a REST callback API.
Repositories

Architecture

Flow Summary
Step 1 – POS Awesome initiates payment
The cashier triggers a card payment from POS Awesome, sending transaction details to the Frappe server:
{
"user": "husna123",
"amount": 1250.00,
"invoice_number": "INV-001",
"customer": "John Doe"
}
Step 2 – Frappe server maps user to device
The Frappe backend looks up the GEIdea Device Map to find the terminal assigned to that user, resolves the device ID and MQTT broker URL, then publishes the payload to the MQTT broker:
{
"device_id": "CCM-987654",
"amount": 1250.00,
"customer": "John Doe",
"invoice_number": "INV-001",
"broker_url": "mqtt://broker.example.com:1883"
}
Step 3 – MQTT broker routes message to terminal
The credit card machine subscribes to its dedicated MQTT topic. The broker delivers the payment request, including invoice number and amount, directly to the terminal.
Step 4 – Terminal processes payment & publishes result
The GEIDEA terminal (running the geidea-claudion Android app) processes the card transaction and publishes the result back to the MQTT broker:
{
"invoice_number": "INV-20250822-001",
"status": "approved",
"transaction_id": "TXN-78"
}
Step 5 – Frappe receives result & responds to POS Awesome
The Frappe server picks up the status from the broker, logs the transaction in GEIdea Log, and returns the final result to POS Awesome:
{
"invoice_number": "INV-20250822-001",
"status": "approved",
"transaction_id": "TXN-78"
}
Components
1. Backend – cardpay_erpgulf (Frappe App)
Key Doctypes
Doctype | Purpose |
|---|---|
| Global MQTT broker configuration (host, port, protocol, credentials, timeout) |
| Maps an ERPNext user to a specific terminal MQTT topic |
| Audit log of every transaction (input, output, status) |
| Provider settings (GEIDEA), connection type (IP), merchant credentials |
Key API Endpoints
Method | Endpoint | Description |
|---|---|---|
|
| Sends payment request from ERPNext to terminal via MQTT |
|
| Receives payment result from the Android terminal |
send_request_to_device – Request Payload (POS Awesome → Frappe)
{
"uuid": "unique-transaction-id",
"user": "cashier@example.com",
"amount": 100.00,
"invoice_number": "INV-001",
"customer": "John Doe",
"refund": 0,
"transaction_id": "",
"posting_date": ""
}
When
refundis1, bothtransaction_idandposting_dateare required.
MQTT Payload (Frappe → MQTT Broker → Terminal)
The Frappe backend forwards the entire request above to the MQTT broker, and appends two additional fields resolved from the GEIdea Device Map:
{
"uuid": "unique-transaction-id",
"user": "cashier@example.com",
"amount": 100.00,
"invoice_number": "INV-001",
"customer": "John Doe",
"refund": 0,
"transaction_id": "",
"posting_date": "",
"device_topic": "5b548bcb1371d319",
"custom_print_reciept_configuration": 0
}
Field | Source | Description |
|---|---|---|
| POS Awesome | Unique transaction identifier |
| POS Awesome | ERPNext cashier username (used to resolve the device) |
| POS Awesome | Transaction amount |
| POS Awesome | Sales invoice reference |
| POS Awesome | Customer name |
| POS Awesome | ERPNext server URL — used by the terminal to post the payment result back |
| POS Awesome |
|
| POS Awesome | Original transaction ID (required when |
| POS Awesome | Posting date (required when |
| Frappe (Device Map) | Android ID of the terminal — also the MQTT topic |
| Frappe (Device Map) | Receipt print config as integer (e.g. |
This full payload is published to the topic matching the terminal's Android ID. The message is also stored in Redis (keyed by uuid) for audit logging.
Return / Refund Invoices
Return invoices are supported. When POS Awesome processes a return, it sets "refund": 1 in the payload along with the transaction_id of the original invoice. The terminal uses this to initiate a refund against that specific transaction rather than a new payment.
Return payload example:
{
"uuid": "unique-transaction-id",
"user": "cashier@example.com",
"amount": 100.00,
"invoice_number": "RINV-001",
"customer": "John Doe",
"callback_url": "https://your-erpnext.com",
"refund": 1,
"transaction_id": "TXN-78",
"posting_date": "2025-08-22",
"device_topic": "5b548bcb1371d319",
"custom_print_reciept_configuration": 0
}
Validation rules for refunds:
refund: 1→ bothtransaction_idandposting_dateare mandatory. The request will be rejected if either is missing.transaction_idmust be the transaction ID from the original approved payment so GEIDEA can match and reverse it.refund: 0→transaction_idis ignored.
State Management (Redis)
Redis is used to manage in-flight transaction state and prevent duplicate requests:
Key Pattern | Purpose |
|---|---|
| Per-transaction state (TTL: 60 s) |
| Device busy lock – prevents concurrent requests to same terminal (TTL: 60 s) |
The send_request_to_device API polls Redis every second, up to the configured Time-Span (default: 60 s), waiting for the callback to populate a response.
MQTT Configuration (MQTT Setting Doctype)
Field | Example Value |
|---|---|
Protocol |
|
Broker Host |
|
Port | Provided by Claudion team |
Username |
|
Password | (stored securely) |
Time-Span |
|
2. Frontend – geidea-claudion (Android App)

An Android application installed on the GEIDEA credit card terminal. It:
Connects to the MQTT broker using credentials entered in its built-in settings page.
Subscribes to its own MQTT topic (derived from the device's Android ID) on startup.
Receives payment request payloads from ERPNext.
Initiates the payment flow on the GEIDEA terminal hardware.
Posts the transaction result back to ERPNext via HTTP.
How the App Selects Its MQTT Server
The MQTT broker is not hardcoded. After the app is installed from the GEIDEA App Store, an administrator opens the in-app Settings page and enters the broker details manually:
Setting | Description |
|---|---|
Broker Host | MQTT server hostname (e.g. |
Port | Provided by Claudion team |
Username | Provided by Claudion team |
Password | Provided by Claudion team |
Callback URL | ERPNext server URL for posting payment results back |
These credentials are saved on the device and used every time the app starts to establish the MQTT connection.
Important: The MQTT broker details are provided by the Claudion team. Contact support@erpgulf.com to obtain them. Only KSA-hosted, SSL-secured brokers are supported.
⚠️ Inter-App Communication: On the GEIDEA terminal, you must enable inter-app communication in the device settings. Without this, the geidea-claudion app will not be able to communicate with the default MADA payment app on the terminal, and payments will fail.
How the App Knows Which MQTT Topic to Subscribe To
The app subscribes to a topic based on its own Android ID — a unique identifier the device already knows without any external input. This is displayed on the app's main screen (e.g. 5b548bcb1371d319).
On the ERPNext side, this same Android ID is entered into the Device Topic field of the GEIdea Device Map doctype and linked to the cashier user who operates that terminal. This is the one-time manual setup step that ties a physical machine to an ERPNext user.
The map record has three key fields:
Field | Description |
|---|---|
Device Enabled | Must be checked for the device to receive requests |
User | ERPNext cashier account (e.g. |
Device Topic | The Android ID of the terminal (e.g. |
Print Receipt Configuration | Controls receipt printing behaviour on the terminal |
In the backend code, this is resolved as:
topic = device_doc.data # Device Topic field = Android ID = MQTT topic
So when ERPNext receives "user": "husna123" from POS Awesome, it queries the device map, retrieves the Android ID as the topic, and publishes the payment payload to that topic on the broker. The terminal — already subscribed to its own Android ID — receives it instantly.
Cashier user ──► GEIdea Device Map ──► Android ID == MQTT Topic
(e.g. husna123) (ERPNext lookup) (e.g. 5b548bcb1371d319)
Callback API Call (Android → ERPNext)
POST {callback_url}/api/method/geidea_erpgulf.geidea_erpgulf.posaw_test.device_callback
Content-Type: application/json
{
"uuid": "unique-transaction-id",
"responseCode": "00",
"responseMessage": "Approved",
...
}
The app uses OkHttp for HTTP and Paho MQTT (or equivalent) for broker communication.
Installation
Backend (ERPNext App)
# From your Frappe bench directory
bench get-app https://github.com/ERPGulf/cardpay_erpgulf
bench --site your-site.com install-app cardpay_erpgulf
bench --site your-site.com migrate
Dependencies:
paho-mqtt– MQTT clientredis– State management
pip install paho-mqtt redis
Android App
The Android app is not available on the Google Play Store or Apple App Store, and cannot be side-loaded manually onto the terminal.
It is distributed exclusively through the GEIDEA App Store, which is available on GEIDEA-provided terminals only. To get the app installed on your terminal, contact your GEIDEA account representative or reach out to support@erpgulf.com.
Configuration
Step 1 – MQTT Setting (ERPNext)
Navigate to MQTT Setting in ERPNext and fill in:
Protocol:
sslBroker Host: your MQTT broker hostname (provided by Claudion — see below)
Port: Provided by Claudion team
Username / Password: broker credentials (provided by Claudion)
Time-Span: polling timeout in seconds (e.g.,
60)
Important: The MQTT server is provided by the Claudion team. Contact support@erpgulf.com to request access. Only KSA-hosted, SSL-secured servers are supported — do not use unverified or non-KSA servers.
Step 2 – GEIdea Device Map (ERPNext)
Create a record mapping each cashier user to their terminal's MQTT topic:
Field | Description |
|---|---|
User | ERPNext user (cashier) |
Data (Topic) | MQTT topic for this terminal |
Device Enabled | Must be checked ( |
Print Receipt Configuration | Receipt format (e.g., |
Step 3 – CardPay Settings (ERPNext)
Navigate to CardPay Settings → New and configure:
Provider:
GEIDEAConnection Type:
IPMachine IP, Merchant ID, API Key, Secret Key
Step 4 – Android App
Configure the app with:
MQTT broker credentials (matching MQTT Setting above)
ERPNext server callback URL
Transaction Status
Status | Meaning |
|---|---|
| Payment successful |
| Payment rejected by the terminal or bank |
| No response from device within the configured Time-Span |
| Another transaction is already in progress on this device |
| Device is not enabled in GEIdea Device Map |
| No device mapping found for the given user |
Logging
Every transaction is logged in the GEIdea Log doctype with:
uuid– Unique transaction identifierinput_response– Original request payloadoutput_response– MQTT publish payloadcustom_status_– Full response JSONcustom_status_of_payment–Approved/Declined/Timeout
Security Notes
The MQTT broker uses SSL/TLS with certificate verification.
ERPNext stores the MQTT password using Frappe's encrypted password field (
get_password).The
device_callbackendpoint is open (allow_guest=True) since terminals do not have ERPNext sessions; validate by UUID and short TTL to mitigate abuse.Redis keys auto-expire after 60 seconds to prevent stale state.
Supported Payment Cards
Accepted card schemes are determined by each merchant's individual arrangement with GEIDEA. Generally, MADA cards are accepted by default. Support for Visa, Mastercard, and other international schemes depends on your GEIDEA account setup. Contact your GEIDEA account manager for details.
License & Copyright
This software is proprietary and copyright protected. It is not open-source.
© ERPGulf & Claudion. All rights reserved. Unauthorized copying, distribution, or modification is strictly prohibited.
For licensing inquiries, visit erpgulf.com or claudion.com.
Contact
Support | |
Sales | |
Website |





