diff --git a/configs/bank-landing/index.html b/configs/bank-landing/index.html
index 2e4cefc..1897f42 100644
--- a/configs/bank-landing/index.html
+++ b/configs/bank-landing/index.html
@@ -1348,8 +1348,8 @@ tw balance
- Your account · bank UI login
- https://bank.hacktivism.ch/webui/
+ Your account · payto
+ —
@@ -1776,8 +1776,8 @@ tw balance
webuiA.setAttribute("target", "_blank");
webuiA.setAttribute("rel", "noopener noreferrer");
}
- /* payto must include host:port (e.g. bank.hacktivism.ch:443) */
- var payto = d.payto_uri || "";
+ /* Account payto (libeufin style) — this is what the account QR encodes */
+ var payto = d.payto_uri || d.qr_payload || "";
if (payA) {
if (payto && payto.indexOf("payto://") === 0) {
payA.href = payto;
@@ -1793,26 +1793,31 @@ tw balance
st.textContent =
"Account created for you · " +
(d.username || d.name || "") +
- " · save username & password";
+ " · save username & password · QR = payto account";
}
- /* Fancy QR_Taler: encode bank UI URL (login with credentials above) */
+ /* QR encodes payto:// of this account (not the generic webui homepage) */
var qrBlock = document.getElementById("auto-account-qr-block");
var qrLabel = document.getElementById("qr-auto-acct-label");
+ var qrPayload = payto || webui;
if (qrBlock) qrBlock.hidden = false;
if (qrLabel) {
qrLabel.textContent =
- "QR_Taler · open bank UI · login as " + (d.username || "you");
+ "Account payto · " + (d.username || "you");
}
var autoPayload = document.getElementById("qr-auto-acct-payload");
if (autoPayload) {
- autoPayload.href = webui;
- autoPayload.textContent = webui;
- autoPayload.setAttribute("target", "_blank");
- autoPayload.setAttribute("rel", "noopener noreferrer");
+ autoPayload.href = qrPayload;
+ autoPayload.textContent = qrPayload;
+ if (qrPayload.indexOf("http") === 0) {
+ autoPayload.setAttribute("target", "_blank");
+ autoPayload.setAttribute("rel", "noopener noreferrer");
+ } else {
+ autoPayload.removeAttribute("target");
+ }
}
makeQr(
document.getElementById("qr-auto-acct"),
- webui,
+ qrPayload,
document.getElementById("qr-auto-acct-logo")
);
if (btn) {
diff --git a/scripts/taler-bank/demo-withdraw-api.py b/scripts/taler-bank/demo-withdraw-api.py
index e226518..12c0785 100755
--- a/scripts/taler-bank/demo-withdraw-api.py
+++ b/scripts/taler-bank/demo-withdraw-api.py
@@ -39,30 +39,38 @@ LANDING = Path(os.environ.get("LANDING_DIR", "/var/www/bank-landing"))
LISTEN = ("127.0.0.1", int(os.environ.get("DEMO_WITHDRAW_PORT", "19096")))
-def bank_payto_authority() -> str:
- """Host:port for payto://x-taler-bank/… (port required; public HTTPS → :443)."""
+def bank_payto_host() -> str:
+ """Hostname for payto://x-taler-bank/HOST/account (libeufin style: no :443)."""
override = os.environ.get("BANK_PAYTO_HOST", "").strip()
if override:
+ # allow full host or host:port if operator sets it
return override
u = urllib.parse.urlparse(
BANK_PUBLIC if "://" in BANK_PUBLIC else f"https://{BANK_PUBLIC}"
)
- host = u.hostname or "bank.hacktivism.ch"
- if u.port:
- port = u.port
- elif (u.scheme or "https") == "https":
- port = 443
- else:
- port = 80
- return f"{host}:{port}"
+ return u.hostname or "bank.hacktivism.ch"
-def make_account_payto(username: str) -> str:
- """Valid x-taler-bank payto with explicit port + encoded receiver-name."""
- auth = bank_payto_authority()
+def make_account_payto(username: str, bank_payto: str = "") -> str:
+ """Account payto for QR/link.
+
+ Prefer libeufin internal_payto_uri when present (authoritative).
+ Fallback matches bank registration: host without default HTTPS port.
+ """
rn = urllib.parse.quote(username, safe="")
- # path account name is bank-safe [a-z0-9-]; keep unencoded
- return f"payto://x-taler-bank/{auth}/{username}?receiver-name={rn}"
+ if isinstance(bank_payto, str) and bank_payto.startswith("payto://x-taler-bank/"):
+ # Keep bank-issued host/account; ensure receiver-name is encoded
+ try:
+ # payto://x-taler-bank/HOST/ACCOUNT?…
+ rest = bank_payto[len("payto://x-taler-bank/") :]
+ host_acct, _, _qs = rest.partition("?")
+ host, _, acct = host_acct.partition("/")
+ if host and acct:
+ return f"payto://x-taler-bank/{host}/{acct}?receiver-name={rn}"
+ except Exception:
+ pass
+ host = bank_payto_host()
+ return f"payto://x-taler-bank/{host}/{username}?receiver-name={rn}"
def public_webui_url() -> str:
@@ -240,8 +248,13 @@ def create_personal_account() -> dict:
)
if code not in (200, 201, 204):
raise RuntimeError(f"register failed HTTP {code}: {body}")
- # Always emit a correct public payto (libeufin often omits :443 → invalid for HTTPS)
- payto = make_account_payto(username)
+ bank_payto = ""
+ if isinstance(body, dict):
+ bank_payto = (
+ body.get("internal_payto_uri") or body.get("payto_uri") or ""
+ )
+ # Prefer bank-issued payto (no fake :443). QR encodes this account payto.
+ payto = make_account_payto(username, bank_payto)
webui = public_webui_url()
return {
"ok": True,
@@ -253,12 +266,14 @@ def create_personal_account() -> dict:
"balance": "GOA:0",
"balance_note": "Starts at zero — not the shared community pool.",
"payto_uri": payto,
+ "qr_payload": payto,
"webui": webui,
"account_url": webui,
"hint": (
"This bank account was created for you automatically "
f"({username}). "
- "Copy username and password now — we do not store them for later recovery."
+ "Copy username and password now — we do not store them for later recovery. "
+ "QR / blue link is the account payto:// (not the bank UI homepage)."
),
"created": time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()),
"created_human": time.strftime("%Y-%m-%d %H:%M %Z", time.localtime()),
diff --git a/scripts/taler-monitoring/check_urls.sh b/scripts/taler-monitoring/check_urls.sh
index 0feb5ce..d269e7c 100755
--- a/scripts/taler-monitoring/check_urls.sh
+++ b/scripts/taler-monitoring/check_urls.sh
@@ -189,29 +189,31 @@ except Exception as e:
if not d.get("ok"):
print("ok!=true")
sys.exit(1)
-payto = d.get("payto_uri") or ""
+payto = d.get("payto_uri") or d.get("qr_payload") or ""
webui = d.get("webui") or d.get("account_url") or ""
-# payto://x-taler-bank/host:port/account?...
+# payto://x-taler-bank/HOST[/port]/account?receiver-name=…
+# Match libeufin: host without :443 is correct for x-taler-bank payto
m = re.match(r"^payto://x-taler-bank/([^/?#]+)/([^/?#]+)(\?.*)?$", payto)
if not m:
print("payto shape invalid:", payto[:120])
sys.exit(1)
auth, acct = m.group(1), m.group(2)
-if ":" not in auth:
- print("payto missing port (need host:port):", auth)
+if not auth or not acct:
+ print("payto host/account empty")
sys.exit(1)
-host, port_s = auth.rsplit(":", 1)
-if not host or not port_s.isdigit():
- print("payto host:port invalid:", auth)
+if "receiver-name=" not in (m.group(3) or ""):
+ print("payto missing receiver-name")
sys.exit(1)
-if not acct:
- print("payto account empty")
+# QR must encode payto (not only webui homepage)
+qr = d.get("qr_payload") or payto
+if not qr.startswith("payto://"):
+ print("qr_payload must be payto:// account URI:", (qr or "")[:80])
sys.exit(1)
u = urlparse(webui)
if u.scheme not in ("http", "https") or not u.netloc or "webui" not in (u.path or ""):
print("webui not absolute https …/webui/:", webui[:120])
sys.exit(1)
-print(f"user={d.get('username','')} payto={auth}/{acct} webui={webui}")
+print(f"user={d.get('username','')} payto={auth}/{acct} qr=payto webui={webui}")
sys.exit(0)
PY
then