diff --git a/scripts/taler-bank/demo-withdraw-api.py b/scripts/taler-bank/demo-withdraw-api.py index e0e6832..e226518 100755 --- a/scripts/taler-bank/demo-withdraw-api.py +++ b/scripts/taler-bank/demo-withdraw-api.py @@ -25,17 +25,50 @@ import ssl import string import time import urllib.error +import urllib.parse import urllib.request from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path 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") AMOUNT = os.environ.get("AMOUNT", "GOA:10") 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).""" + 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: p = os.environ.get("BANK_PASS", "").strip() if p: @@ -98,10 +131,11 @@ def mint_withdraw() -> dict: raise RuntimeError(f"no taler_withdraw_uri: {wd}") if not wid: wid = uri.rstrip("/").split("/")[-1] - # wallets often prefer no default HTTPS port in host - uri_clean = uri.replace(":443/", "/").replace(":443?", "?") + # Keep host:port from libeufin (e.g. bank.hacktivism.ch:443). Stripping :443 + # breaks taler-integration withdraw links / main landing QR on HTTPS banks. + uri = str(uri).strip() 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.created").write_text( 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") return { "ok": True, - "taler_withdraw_uri": uri_clean, + "taler_withdraw_uri": uri, "withdrawal_id": wid, "amount": AMOUNT, "pool_account": USER, + "taler_integration_base": f"{BANK_PUBLIC}/taler-integration/", "hint": "Open in GNU Taler Wallet (iOS/Android/desktop). No bank registration.", "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): raise RuntimeError(f"register failed HTTP {code}: {body}") - payto = "" - if isinstance(body, dict): - payto = body.get("internal_payto_uri") or body.get("payto_uri") or "" - if not payto: - payto = f"payto://x-taler-bank/bank.hacktivism.ch/{username}?receiver-name={username}" + # Always emit a correct public payto (libeufin often omits :443 → invalid for HTTPS) + payto = make_account_payto(username) + webui = public_webui_url() return { "ok": True, "created_for_you": True, @@ -220,7 +253,8 @@ def create_personal_account() -> dict: "balance": "GOA:0", "balance_note": "Starts at zero — not the shared community pool.", "payto_uri": payto, - "webui": "https://bank.hacktivism.ch/webui/", + "webui": webui, + "account_url": webui, "hint": ( "This bank account was created for you automatically " f"({username}). "