← Dashboard

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.

SimuPay provides three integration points for your payment terminal or application:
Android Logging SDK Reporting API API Reference
Transaction ProtocolConnect to the APACS or Amex host over TLS to simulate acquirer transactions end-to-end.
Logging SDKDrop-in Android library that intercepts android.util.Log calls and streams them to the dashboard in real time.
Reporting APIHTTP endpoint that accepts custom JSON payloads for building charts, KPIs, and tables on the Reports dashboard.

Quick Start

  1. Create an account — Sign up at simupay.co.uk and log in to the dashboard.
  2. Add a Terminal ID — Register one or more 8-digit Terminal IDs in the dashboard. These link all data to your account.
  3. Choose your integration — Use the Android Logging SDK, the Reporting API, or both together.
  4. 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:

Web Dashboard
https://simupay.co.uk
APACS Host
simupay.co.uk:20000
Amex Host
simupay.co.uk:44638
Log Endpoint
POST https://simupay.co.uk/api/log
Report Endpoint
POST https://simupay.co.uk/api/report-ingest
WebSocket
wss://simupay.co.uk/ws
TLS: The APACS and Amex hosts require TLS connections. All HTTPS and WSS endpoints use standard TLS on port 443.

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.

1

Add JitPack and the plugin resolver

Open your project's settings.gradle and add the JitPack repository to both pluginManagement and dependencyResolutionManagement:

settings.gradle
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' }
    }
}
2

Apply the plugin and add the dependency

In your app module's build.gradle, apply the SimuPay plugin and add the library dependency:

app/build.gradle
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'
}
Note: Your app must target minSdk 26 or higher. The plugin requires Android Gradle Plugin 7.0+.
3

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.

Manual alternative: If you prefer not to use the plugin, you can manually replace import android.util.Log; with import com.etl.simupaylog.Log; in each file and omit the plugin from your build.gradle.
4

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.

MyApplication.java
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");
    }
}
Tip: Store the Terminal ID in your app's configuration or SharedPreferences rather than hard-coding it, so the same APK can be deployed to multiple terminals with different IDs.
5

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.

Example – signature capture
import com.etl.simupaylog.Log;

// With a caption
Log.img("SignatureTask", "Customer signature", signatureBitmap);

// Without a caption
Log.img("Camera", cardScanBitmap);
6

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.

Example – capture full activity
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.

1

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.

Required field: tid — your 8-digit Terminal ID. This links the report to your account.
2

Field conventions

You can send any JSON fields you like — SimuPay stores them all. However, certain field names are recognised by the dashboard widgets:

FieldTypeDescription
tidstringRequired. 8-digit Terminal ID that links this report to a user account.
amountnumberTransaction amount in pence (e.g. 2499 = £24.99). Used for sum/avg aggregation.
outcomestringTransaction result: APPROVED, DECLINED, PARTIAL, TIMEOUT, ERROR.
response_codestringAcquirer response code (e.g. "00", "05").
elapsed_msnumberRound-trip time in milliseconds. Useful for latency charts.
batterynumberDevice battery percentage (0–100). Used by gauge widgets.
signal_strengthnumberSignal strength in dBm (e.g. -62). Used by gauge widgets.
temperaturenumberDevice temperature in °C.
statusstringDevice status (e.g. "active", "idle", "offline"). Used by status LED widgets.
protocolstringProtocol used: APACS or ISO8583.
sourcestringIdentifies the sending application (e.g. "SimuPayDemo", "POS-v3").
any otheranyCustom fields are stored and available for charting. Use numeric values for sum/avg/min/max aggregation.
3

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_amount
  • outcome, response_code, auth_code, response_text
  • elapsed_ms, card_pan_last4 (last 4 digits only — never the full PAN)
  • timestamp (ISO 8601 UTC), source ("SimuPayDemo")
4

Query reports — authenticated endpoints

These endpoints require a session cookie (log in first). They power the Reports dashboard but can also be called directly.

MethodEndpointDescription
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)
fnsum | avg | min | max | count | last
from, to — unix timestamps
bucket — bucket size in seconds (default 3600)
group_bytime (default) or tid
tid — 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

bash
# 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:

python
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 success

Node.js

javascript
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+):

java
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:

CodeMeaning
00Approved
03Invalid merchant
04Pick up card
05Do not honour (declined)
09Request in progress (reversal)
10Partial approval
51Insufficient funds
59Suspected fraud
95Reconciliation accepted
96System 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:

websocket
// 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:

TypeDescription
transactionA transaction was processed. Includes terminal_id, timestamp, amount, outcome, and response code.
logA log entry was received from the Android SDK. Includes terminal_id, timestamp, tag, level, and message.
reportA report was ingested. Includes terminal_id, timestamp, and all custom fields from the report payload.
Filtering: Pass one or more Terminal IDs as a comma-separated 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:

Logs not appearing on the dashboard

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.

Connection refused

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.

Reports not showing on the dashboard

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 disconnects

WebSocket connections are closed after a period of inactivity. This is normal. Implement automatic reconnection logic in your client. The dashboard handles this automatically.