Small shops that take bank transfers usually confirm them by hand: the bank app shows "deposit received", someone opens the order list and marks the order paid. Banks do offer APIs for this, but they come with contracts and fees that a small shop doesn't want.
The phone already gets the information for free, as a notification. So I wired the notification straight into code.
The setup
I used Droidline, an open source tool I've been building. It has two parts:
- an Android app that reads the screen and notifications through Android's accessibility and notification access, using only permissions an ordinary app can get
- a server on the PC (
droidline serve) that the app connects to over Wi-Fi
There's no ADB, no USB debugging and no root. You pair the phone once by scanning a QR code from droidline pair.
The code
The same two steps in every language: tell the phone which app's notifications to forward, then react to each one.
Python
from droidline import connect
d = connect("s23")
d.notify_filter(["com.example.bank"]) # only pass this app's notifications to the PC
def paid(n):
if n["text"].startswith("[Deposit]"):
mark_paid(n["text"]) # your own order system
d.on_notification(callback=paid)
input()
Node.js
import { connect } from "droidline";
const d = await connect("s23");
await d.notifyFilter(["com.example.bank"]);
await d.onNotification({ package: "com.example.bank" }, (n) => {
if (n.text.startsWith("[Deposit]")) markPaid(n.text);
});
C
using Droidline;
await using var d = await DroidlineClient.ConnectAsync("s23");
await d.NotifyFilterAsync(new[] { "com.example.bank" });
using var stop = await d.OnNotificationAsync(n =>
{
if (n.Text.StartsWith("[Deposit]")) MarkPaid(n.Text);
}, package: "com.example.bank");
Console.ReadLine();
Shell
droidline notify_filter com.example.bank
droidline on_notification --package com.example.bank
The second command prints each notification as a JSON line until you stop it, so you can pipe it into anything.
notify_filter matters: by default nothing leaves the phone, and you choose which apps' notifications are forwarded. Replace com.example.bank with your bank app's package name, and the [Deposit] check with your bank's wording, since every bank phrases the notification differently. mark_paid stands for your own order system.
Install: pip install droidline, npm install droidline or dotnet add package Droidline. The droidline command and the Android app are on the releases page.
Things to know
- The phone has to stay on and connected. An old phone on a charger works fine for this.
- The connection is encrypted end to end (P-256 and AES-256-GCM), so the notification text never crosses the network in the clear.
- Android 15 and later hide one-time codes inside notifications from apps. Deposit notifications are not affected, but if you try the same trick for verification codes, it only works on Android 14 and earlier.
Beyond notifications
The same connection can tap, type, swipe, take screenshots and run OCR, and there's an MCP server so AI agents can use the phone. On a Galaxy S23 Ultra, finding an element by its text takes 16 ms (median of 10 runs), against 100 ms for Appium UiAutomator2 and 2,368 ms for adb plus uiautomator. The benchmark script is in the repo under scripts/bench.
- GitHub: https://github.com/KnifeLemon/Droidline (MIT)
- Docs: https://droidline.dev/docs/
I wrote this post with help from an AI assistant and checked every command against the project.
Top comments (2)
I'd keep mark_paid behind a reconciliation step rather than the text-prefix check alone. A useful fixture: two open orders for the same amount, one deposit notification, then the same notification delivered again after reconnect. Expect one unmatched payment candidate, not two paid orders. Where the bank exposes a transaction reference, preserve it separately from the notification ID; where it doesn't, flag the match for review. How does Droidline distinguish a newly received notification from an update or replay of an existing one?
Leaving an old phone on a charger for notification scraping works well until the OEM battery killer or notification bundling kicks in. When two deposits land within a few seconds, several banking apps collapse the individual alert text into an aggregated group summary, which hides the deposit amount in the extras bundle unless you traverse the child notifications explicitly.