[2] No IPC, no bound service, no content provider. The messenger
opens talerlink://…, the wallet answers with
<scheme>://taler-return?…. <scheme> is the return scheme of
the messenger: sgnl for the official Signal app (default entry upstream),
signalfuergnu for the test fork, talermost for Mattermost.
| Link | Direction | Purpose |
|---|---|---|
| compose-send | messenger → wallet | Create a payment: send or request, optionally split across a group |
| compose-refund | messenger → wallet | Refund a received, accepted payment |
| compose-abort | messenger → wallet | Cancel the user's own open payments (1 to 32 URIs) |
| pay-push / pay-pull | messenger → wallet | Action link: the normal payment URI plus correlationId and returnUri; accept a foreign payment |
| taler-return | wallet → messenger | Return: status variant for compose-send, compose-refund and action links; result variant for compose-abort |
| open / taler-open | wallet → messenger | Open the chat at the payment message from the wallet's transaction list |
messenger wallet
| remember(correlationId, intent) |
|------ talerlink://compose-* ------------->| parse, user confirms
|------ talerlink://pay-push/...?… -------->| read and strip query, normal URI flow
|<----- <scheme>://taler-return?… ---------| target verified, query rebuilt
take(correlationId) (single use, 15 min)
re-check against the exchange, send message / draft / update status
Amount, currency and subject are asked by the wallet; the messenger knows no amounts.
The user picks send or request in the wallet; direction is only the initial
position of the toggle.
| Parameter | Required | Meaning |
|---|---|---|
| recipientHint | no | Display name of the chat. Display only |
| senderAppName | no | Name of the messenger, e.g. Signal. Used only for the subject proposal send via <name>: <hint> |
| isGroup | no | true or false |
| direction | no | PAY_PUSH (send, default) or PAY_PULL (request) |
| disappearingMessagesSeconds | no | Timer of the chat, 0 = off |
| correlationId | yes | Lowercase UUID, new per operation, echoed back unchanged |
| returnUri | yes | Exactly <scheme>://taler-return, no path, query or fragment |
| memberCount | groups | Group size according to the messenger. A hint, the wallet does not rely on it |
| avatarUri | no | Android: content:// URI of the chat avatar with a temporary read grant. The wallet copies a scaled-down version |
| avatarFile | no | iOS: plain file name <correlationId>.jpg in a shared app group folder. Name checked, max. 512 KB, source deleted after copying |
talerlink://compose-send?recipientHint=Anna%20M%C3%BCller&senderAppName=Signal
&isGroup=false&direction=PAY_PUSH&disappearingMessagesSeconds=0
&correlationId=6f1c2a34-8b1e-4d7a-9c55-0e8f3b2a1d90
&returnUri=sgnl%3A%2F%2Ftaler-return
Returns the status variant with status=ready,
talerUri and talerPaymentData (one URI for a single payment, N for a
group split), or status=cancelled if the user leaves before a payment exists.
originalUri, correlationId, returnUri. The wallet
resolves the received transaction itself and prefills amount and subject. It warns if the
payment was already refunded. Returns ready + talerUri.
v, repeated uri (1 to 32), optional display metadata
(reason, summary, totalAmount, …),
correlationId, returnUri. One confirmation for all. Metadata is
never used for decisions; the wallet warns when it disagrees with its own transactions.
talerlink://pay-push/<rest>?correlationId=…&returnUri=…. The wallet
reads both parameters, strips the whole query and hands the clean taler:// URI
to wallet-core. Own URI: opens the own transaction instead of the accept screen.
<scheme>://taler-return?correlationId=<id>
&status=ready|cancelled
[&talerUri=<uri>]
[&talerPaymentData=<json>]
For compose-send, compose-refund and action links. For action links
status is only a trigger: the messenger re-checks the payment at the exchange.
<scheme>://taler-return?correlationId=<id>
&result=<uri>%7Caborted
&result=<uri>%7Calready_claimed
One result per input URI, in input order:
aborted, already_claimed, expired,
declined, not_own, failed. Split at the first
|.
| Android | <scheme>://taler-return?open=<talerUri>, explicit intent to the verified package that sent the payment |
| iOS | <scheme>://taler-open?v=1&uriHash=<sha256>&uriHash=…. Only the hash leaves the wallet, because iOS cannot verify which app owns a scheme |
| Messenger | opens only a payment it knows itself, jumps to the payment message, asks the exchange for the status right away. Never a value from the link |
A-Z a-z 0-9 - . _ ~ stay raw. Space is %20, never +.uri=…&uri=…), order kept.pay-push/pay-pull URI is a bearer token: never logged, not in error messages or crash reports.<scheme>://taler-return and always rebuilds the query. Android checks package and signing certificate against AllowedCallers.correlationId and trusts no value in it: amount and status come from the exchange.recipientHint or memberCount are untrusted and only displayed.The full contract with error cases, data model and decision log lives
with the code, in German:
docs/taler-signal/
in the iOS wallet repository (link-protokoll.md, datenmodell.md,
entscheidungen.md).