bank: fix broken taler-integration withdraw and payto URIs (keep :443)

Was broken: demo-withdraw stripped :443 from libeufin’s taler://withdraw
host, so the main bank.hacktivism.ch QR / taler-integration link failed.
Also emit auto-account payto://x-taler-bank/host:443/… and absolute
https://bank.hacktivism.ch/webui/ (no relative /intro/ base confusion).
This commit is contained in:
Hernâni Marques 2026-07-10 22:12:00 +02:00
parent c916811eba
commit 9d45a9ba66
No known key found for this signature in database

View file

@ -25,17 +25,50 @@ import ssl
import string import string
import time import time
import urllib.error import urllib.error
import urllib.parse
import urllib.request import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path from pathlib import Path
BANK = os.environ.get("BANK_URL", "http://127.0.0.1:9012").rstrip("/") BANK = os.environ.get("BANK_URL", "http://127.0.0.1:9012").rstrip("/")
# Public HTTPS base (Caddy) — used for webui links and x-taler-bank payto host:port
BANK_PUBLIC = os.environ.get("BANK_PUBLIC", "https://bank.hacktivism.ch").rstrip("/")
USER = os.environ.get("BANK_USER", "explorer") USER = os.environ.get("BANK_USER", "explorer")
AMOUNT = os.environ.get("AMOUNT", "GOA:10") AMOUNT = os.environ.get("AMOUNT", "GOA:10")
LANDING = Path(os.environ.get("LANDING_DIR", "/var/www/bank-landing")) LANDING = Path(os.environ.get("LANDING_DIR", "/var/www/bank-landing"))
LISTEN = ("127.0.0.1", int(os.environ.get("DEMO_WITHDRAW_PORT", "19096"))) 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)."""
override = os.environ.get("BANK_PAYTO_HOST", "").strip()
if override:
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}"
def make_account_payto(username: str) -> str:
"""Valid x-taler-bank payto with explicit port + encoded receiver-name."""
auth = bank_payto_authority()
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}"
def public_webui_url() -> str:
return f"{BANK_PUBLIC}/webui/"
def load_pass() -> str: def load_pass() -> str:
p = os.environ.get("BANK_PASS", "").strip() p = os.environ.get("BANK_PASS", "").strip()
if p: if p:
@ -98,10 +131,11 @@ def mint_withdraw() -> dict:
raise RuntimeError(f"no taler_withdraw_uri: {wd}") raise RuntimeError(f"no taler_withdraw_uri: {wd}")
if not wid: if not wid:
wid = uri.rstrip("/").split("/")[-1] wid = uri.rstrip("/").split("/")[-1]
# wallets often prefer no default HTTPS port in host # Keep host:port from libeufin (e.g. bank.hacktivism.ch:443). Stripping :443
uri_clean = uri.replace(":443/", "/").replace(":443?", "?") # breaks taler-integration withdraw links / main landing QR on HTTPS banks.
uri = str(uri).strip()
LANDING.mkdir(parents=True, exist_ok=True) LANDING.mkdir(parents=True, exist_ok=True)
(LANDING / "withdraw.uri").write_text(uri_clean + "\n") (LANDING / "withdraw.uri").write_text(uri + "\n")
(LANDING / "withdraw.amount").write_text(AMOUNT + "\n") (LANDING / "withdraw.amount").write_text(AMOUNT + "\n")
(LANDING / "withdraw.created").write_text( (LANDING / "withdraw.created").write_text(
time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()) + "\n" time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()) + "\n"
@ -114,10 +148,11 @@ def mint_withdraw() -> dict:
watch.write_text("\n".join(sorted(ids)) + "\n") watch.write_text("\n".join(sorted(ids)) + "\n")
return { return {
"ok": True, "ok": True,
"taler_withdraw_uri": uri_clean, "taler_withdraw_uri": uri,
"withdrawal_id": wid, "withdrawal_id": wid,
"amount": AMOUNT, "amount": AMOUNT,
"pool_account": USER, "pool_account": USER,
"taler_integration_base": f"{BANK_PUBLIC}/taler-integration/",
"hint": "Open in GNU Taler Wallet (iOS/Android/desktop). No bank registration.", "hint": "Open in GNU Taler Wallet (iOS/Android/desktop). No bank registration.",
"created": time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()), "created": time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()),
} }
@ -205,11 +240,9 @@ def create_personal_account() -> dict:
) )
if code not in (200, 201, 204): if code not in (200, 201, 204):
raise RuntimeError(f"register failed HTTP {code}: {body}") raise RuntimeError(f"register failed HTTP {code}: {body}")
payto = "" # Always emit a correct public payto (libeufin often omits :443 → invalid for HTTPS)
if isinstance(body, dict): payto = make_account_payto(username)
payto = body.get("internal_payto_uri") or body.get("payto_uri") or "" webui = public_webui_url()
if not payto:
payto = f"payto://x-taler-bank/bank.hacktivism.ch/{username}?receiver-name={username}"
return { return {
"ok": True, "ok": True,
"created_for_you": True, "created_for_you": True,
@ -220,7 +253,8 @@ def create_personal_account() -> dict:
"balance": "GOA:0", "balance": "GOA:0",
"balance_note": "Starts at zero — not the shared community pool.", "balance_note": "Starts at zero — not the shared community pool.",
"payto_uri": payto, "payto_uri": payto,
"webui": "https://bank.hacktivism.ch/webui/", "webui": webui,
"account_url": webui,
"hint": ( "hint": (
"This bank account was created for you automatically " "This bank account was created for you automatically "
f"({username}). " f"({username}). "