Developer Resources
Integrate your Android app or any HTTP client with SimuPay to stream logs, transaction data, and custom reports to your dashboard in real time.
| Transaction Protocol | Connect to the APACS or Amex host over TLS to simulate acquirer transactions end-to-end. |
| Logging SDK | Drop-in Android library that intercepts android.util.Log calls and streams them to the dashboard in real time. |
| Reporting API | HTTP endpoint that accepts custom JSON payloads for building charts, KPIs, and tables on the Reports dashboard. |
Quick Start
- Create an account — Sign up at simupay.co.uk and log in to the dashboard.
- Add a Terminal ID — Register one or more 8-digit Terminal IDs in the dashboard. These link all data to your account.
- Choose your integration — Use the Android Logging SDK, the Reporting API, or both together.
- Start sending data — Logs and reports appear on your dashboard in real time. Connect via WebSocket for live event streaming.
Connection Details
All endpoints for connecting to SimuPay services:
Android Logging SDK
A drop-in replacement for android.util.Log that streams log entries, screenshots and images from your Android app to the SimuPay dashboard in real time.
Add JitPack and the plugin resolver
Open your project's settings.gradle and add the JitPack repository to both pluginManagement and dependencyResolutionManagement:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'com.etl.simupay-log') {
useModule("com.github.EmbeddedTechnologies.simupay-log:plugin:${requested.version}")
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Apply the plugin and add the dependency
In your app module's build.gradle, apply the SimuPay plugin and add the library dependency:
plugins {
id 'com.android.application'
id 'com.etl.simupay-log' version 'v1.2.0'
}
dependencies {
implementation 'com.github.EmbeddedTechnologies.simupay-log:lib:v1.2.0'
}
minSdk 26 or higher. The plugin requires Android Gradle Plugin 7.0+.That's it — no import changes needed
The Gradle plugin automatically redirects all android.util.Log calls to the SimuPay logging library at build time using bytecode instrumentation. Your source code stays completely untouched.
All existing Log.v(), Log.d(), Log.i(), Log.w(), and Log.e() calls will now forward entries to your SimuPay dashboard while still appearing in Logcat as normal.
import android.util.Log; with import com.etl.simupaylog.Log; in each file and omit the plugin from your build.gradle.Configure at startup
Call the configuration methods once in your Application.onCreate() (or equivalent startup point). Your Terminal ID is shown in the SimuPay dashboard header after registration.
import com.etl.simupaylog.Log;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Set your SimuPay endpoint
Log.setSimulatorUrl("https://simupay.co.uk/api/log");
// Set your 8-digit Terminal ID (shown in the dashboard header)
Log.setTerminalId("12345678");
}
}
SharedPreferences rather than hard-coding it, so the same APK can be deployed to multiple terminals with different IDs.Image logging optional
Send a Bitmap to the dashboard as an image log entry. The image is JPEG-compressed and displayed in the Logs view with a View image button. If the image arrives within 2 minutes of a transaction, it is also attached to that transaction card as a receipt.
import com.etl.simupaylog.Log;
// With a caption
Log.img("SignatureTask", "Customer signature", signatureBitmap);
// Without a caption
Log.img("Camera", cardScanBitmap);
Screenshot logging optional
Capture a screenshot of the current screen and send it to the dashboard. Uses PixelCopy (API 26+) for hardware-accelerated capture with an automatic fallback to View.draw(Canvas). Screenshots appear in the Logs view just like image logs.
import com.etl.simupaylog.Log;
// Capture the full activity screen with a caption
Log.screen("Payment", "Transaction complete screen", this);
// Capture without a caption
Log.screen("Payment", this);
// Capture a specific view only
Log.screen("Receipt", "Receipt view", receiptView);
Reporting API
Send custom report data from any device or service. Reports are displayed on the Reports dashboard where you can build charts, KPIs, gauges, and tables from any field you send.
Send a report — POST /api/report-ingest
This is a public endpoint (no authentication required). Send a JSON object with at least a tid field. All other fields are stored as-is and become available for charting and aggregation.
tid — your 8-digit Terminal ID. This links the report to your account.Field conventions
You can send any JSON fields you like — SimuPay stores them all. However, certain field names are recognised by the dashboard widgets:
| Field | Type | Description |
|---|---|---|
tid | string | Required. 8-digit Terminal ID that links this report to a user account. |
amount | number | Transaction amount in pence (e.g. 2499 = £24.99). Used for sum/avg aggregation. |
outcome | string | Transaction result: APPROVED, DECLINED, PARTIAL, TIMEOUT, ERROR. |
response_code | string | Acquirer response code (e.g. "00", "05"). |
elapsed_ms | number | Round-trip time in milliseconds. Useful for latency charts. |
battery | number | Device battery percentage (0–100). Used by gauge widgets. |
signal_strength | number | Signal strength in dBm (e.g. -62). Used by gauge widgets. |
temperature | number | Device temperature in °C. |
status | string | Device status (e.g. "active", "idle", "offline"). Used by status LED widgets. |
protocol | string | Protocol used: APACS or ISO8583. |
source | string | Identifies the sending application (e.g. "SimuPayDemo", "POS-v3"). |
| any other | any | Custom fields are stored and available for charting. Use numeric values for sum/avg/min/max aggregation. |
Android — ReportClient
The SimuPay Demo App includes a ReportClient class that sends transaction reports automatically after each sale. You can use it in your own app:
import com.etl.simupaydemo.reporting.ReportClient; // Create once (derives base URL from the log URL) ReportClient reporter = new ReportClient("https://simupay.co.uk"); // After each transaction, fire and forget: reporter.report(terminalId, merchantId, cardPan, amountPence, protocol, result);
The client runs on a background thread and sends:
tid,mid,protocol,amount,approved_amountoutcome,response_code,auth_code,response_textelapsed_ms,card_pan_last4(last 4 digits only — never the full PAN)timestamp(ISO 8601 UTC),source("SimuPayDemo")
Query reports — authenticated endpoints
These endpoints require a session cookie (log in first). They power the Reports dashboard but can also be called directly.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/report-ingest |
Send a report. Body: JSON object with tid field. Public (no auth). Returns 204. |
GET |
/api/reports |
Fetch raw reports. Query params: from, to (unix timestamps), limit (max 5000), tid (optional filter). Returns JSON array. |
GET |
/api/reports/fields |
List all unique field names across your reports. Returns JSON array of strings. |
GET |
/api/reports/aggregate |
Aggregate a field over time or by TID. Query params:field — field name to aggregate (required)fn — sum | avg | min | max | count | lastfrom, to — unix timestampsbucket — bucket size in seconds (default 3600)group_by — time (default) or tidtid — optional single-TID filter
|
GET |
/api/report-templates |
List your saved dashboard templates. Returns JSON array. |
POST |
/api/report-templates |
Create a dashboard template. Body: {"name": "...", "config": {...}}. |
PUT |
/api/report-templates/{id} |
Update a template. Body: {"name": "...", "config": {...}}. |
DELETE |
/api/report-templates/{id} |
Delete a template. |
Code Examples
Send reports from any language or tool. Below are examples for common environments.
curl
# Send a report via curl
curl -X POST https://simupay.co.uk/api/report-ingest \
-H "Content-Type: application/json" \
-d '{
"tid": "12345678",
"battery": 85,
"signal_strength": -62,
"temperature": 22.5,
"transaction_count": 142,
"amount": 2499,
"outcome": "APPROVED",
"response_code": "00",
"elapsed_ms": 187,
"firmware": "2.1.0",
"status": "active"
}'A successful request returns 204 No Content. The report is immediately available on the dashboard and broadcast to connected WebSocket clients.
Python
Minimal example using only the standard library:
import json, urllib.request
report = {
"tid": "12345678",
"amount": 1500,
"outcome": "APPROVED",
"response_code": "00",
"elapsed_ms": 142,
"device": "PAX-A920",
}
req = urllib.request.Request(
"https://simupay.co.uk/api/report-ingest",
data=json.dumps(report).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req) # 204 No Content on successNode.js
const https = require('https');
const data = JSON.stringify({
tid: '12345678',
amount: 1500,
outcome: 'APPROVED',
response_code: '00',
elapsed_ms: 142,
});
const req = https.request({
hostname: 'simupay.co.uk',
path: '/api/report-ingest',
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
});
req.write(data);
req.end();Java (non-Android)
Using the java.net.http client (Java 11+):
HttpClient client = HttpClient.newHttpClient();
String json = "{\"tid\":\"12345678\",\"amount\":1500,\"outcome\":\"APPROVED\"}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://simupay.co.uk/api/report-ingest"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
client.send(request, HttpResponse.BodyHandlers.ofString());Reference
Response Codes
Common APACS and ISO 8583 acquirer response codes:
| Code | Meaning |
|---|---|
| 00 | Approved |
| 03 | Invalid merchant |
| 04 | Pick up card |
| 05 | Do not honour (declined) |
| 09 | Request in progress (reversal) |
| 10 | Partial approval |
| 51 | Insufficient funds |
| 59 | Suspected fraud |
| 95 | Reconciliation accepted |
| 96 | System error |
Log SDK Methods
| Method | Description |
|---|---|
Log.setSimulatorUrl(String url) |
Set the SimuPay endpoint. Pass null to disable remote forwarding. |
Log.setTerminalId(String tid) |
Set your 8-digit Terminal ID. Included in every log entry so the dashboard can filter by terminal. |
Log.v / d / i / w / e (String tag, String msg) |
Drop-in replacements for android.util.Log. Writes to Logcat and forwards to the dashboard. |
Log.v / d / i / w / e (String tag, String msg, Throwable tr) |
Overloads that also capture and forward the stack trace. |
Log.img(String tag, String message, Bitmap bitmap) |
Send a bitmap as an image log entry with an optional caption. |
Log.img(String tag, Bitmap bitmap) |
Overload with no caption. |
Log.screen(String tag, String message, Activity activity) |
Capture a screenshot of the Activity and send it to the dashboard. Uses PixelCopy with canvas fallback. |
Log.screen(String tag, Activity activity) |
Overload with no caption. |
Log.screen(String tag, String message, View view) |
Capture a specific View and send it to the dashboard. |
Log.getStackTraceString(Throwable tr) |
Delegates to android.util.Log.getStackTraceString. |
Log.isLoggable(String tag, int level) |
Delegates to android.util.Log.isLoggable. |
WebSocket Events
Connect to the WebSocket endpoint for real-time event streaming:
// Connect with optional Terminal ID filter
const ws = new WebSocket('wss://simupay.co.uk/ws?tids=12345678,87654321');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.type, data.terminal_id, data.timestamp);
};Events are JSON objects with a type field indicating the event kind:
| Type | Description |
|---|---|
transaction | A transaction was processed. Includes terminal_id, timestamp, amount, outcome, and response code. |
log | A log entry was received from the Android SDK. Includes terminal_id, timestamp, tag, level, and message. |
report | A report was ingested. Includes terminal_id, timestamp, and all custom fields from the report payload. |
tids query parameter to receive events only for those terminals. Omit the parameter to receive all events for your account.Troubleshooting
Common issues and solutions:
Verify that the Terminal ID set via Log.setTerminalId() matches a TID registered to your account. Check that the URL passed to Log.setSimulatorUrl() is correct. On Android, ensure the INTERNET permission is declared in your manifest.
Check that you are connecting to the correct port (20000 for APACS, 44638 for Amex). Ensure TLS is enabled on your client. If behind a corporate firewall, verify that outbound connections to the SimuPay host and port are allowed.
Confirm that the tid field in your report JSON matches a Terminal ID registered to your account. Reports with unrecognised TIDs are stored but will not appear on any dashboard.
WebSocket connections are closed after a period of inactivity. This is normal. Implement automatic reconnection logic in your client. The dashboard handles this automatically.