taler-monitoring: e2e public template pays by product name

This commit is contained in:
Hernâni Marques 2026-07-10 20:07:27 +02:00
parent 4dae7c082f
commit 250d4ad75b
No known key found for this signature in database

View file

@ -775,26 +775,35 @@ else:
return 1 return 1
} }
# One variable payment amount # One payment: e2e_one_pay AMOUNT [SUMMARY] [TAG]
# SUMMARY defaults to "monitoring pay $AMOUNT"; TAG defaults from amount.
e2e_one_pay() { e2e_one_pay() {
PAY_AMT="$1" PAY_AMT="$1"
local tag local PAY_SUM="${2:-monitoring pay ${PAY_AMT}}"
local tag="${3:-}"
if [ -z "$tag" ]; then
tag=$(printf '%s' "$PAY_AMT" | tr '.:' '__') tag=$(printf '%s' "$PAY_AMT" | tr '.:' '__')
section "e2e · pay $PAY_AMT" fi
e2e_over && { warn "pay" "budget exhausted — skip $PAY_AMT"; return 1; } # shell-safe tag for files
tag=$(printf '%s' "$tag" | tr -c 'A-Za-z0-9._-' '_')
section "e2e · pay $PAY_AMT · $PAY_SUM"
e2e_over && { warn "pay" "budget exhausted — skip $PAY_AMT ($PAY_SUM)"; return 1; }
if [ -z "${MPW:-}" ]; then if [ -z "${MPW:-}" ]; then
warn "pay $PAY_AMT" "no merchant token" warn "pay $PAY_AMT" "no merchant token"
return 1 return 1
fi fi
AUTH="Authorization: Bearer secret-token:${MPW}" AUTH="Authorization: Bearer secret-token:${MPW}"
# JSON-escape summary for curl -d
local SUM_JSON
SUM_JSON=$(python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$PAY_SUM" 2>/dev/null || printf '"%s"' "$PAY_SUM")
curl -skS -m 15 -o "$SCRATCH/ord-$tag.json" -X POST \ curl -skS -m 15 -o "$SCRATCH/ord-$tag.json" -X POST \
-H "$AUTH" -H 'Content-Type: application/json' \ -H "$AUTH" -H 'Content-Type: application/json' \
-d "{\"order\":{\"summary\":\"monitoring pay ${PAY_AMT}\",\"amount\":\"${PAY_AMT}\",\"fulfillment_message\":\"ok\"},\"create_token\":true}" \ -d "{\"order\":{\"summary\":${SUM_JSON},\"amount\":\"${PAY_AMT}\",\"fulfillment_message\":\"ok\"},\"create_token\":true}" \
"${MERCHANT_PUBLIC}/instances/${INST}/private/orders" 2>"$SCRATCH/ord-$tag.err" || true "${MERCHANT_PUBLIC}/instances/${INST}/private/orders" 2>"$SCRATCH/ord-$tag.err" || true
if ! grep -q order_id "$SCRATCH/ord-$tag.json" 2>/dev/null; then if ! grep -q order_id "$SCRATCH/ord-$tag.json" 2>/dev/null; then
if [ "$E2E_REMOTE" != "1" ] && koopa_ssh_ok; then if [ "$E2E_REMOTE" != "1" ] && koopa_ssh_ok; then
koopa_ssh_run 20 \ koopa_ssh_run 20 \
"curl -skS -m 12 -X POST -H 'Authorization: Bearer secret-token:${MPW}' -H 'Content-Type: application/json' -d '{\"order\":{\"summary\":\"monitoring pay\",\"amount\":\"${PAY_AMT}\",\"fulfillment_message\":\"ok\"},\"create_token\":true}' 'https://127.0.0.1:9010/instances/${INST}/private/orders'" \ "curl -skS -m 12 -X POST -H 'Authorization: Bearer secret-token:${MPW}' -H 'Content-Type: application/json' -d '{\"order\":{\"summary\":${SUM_JSON},\"amount\":\"${PAY_AMT}\",\"fulfillment_message\":\"ok\"},\"create_token\":true}' 'https://127.0.0.1:9010/instances/${INST}/private/orders'" \
>"$SCRATCH/ord-$tag.json" 2>"$SCRATCH/ord-$tag.err" || true >"$SCRATCH/ord-$tag.json" 2>"$SCRATCH/ord-$tag.err" || true
fi fi
fi fi
@ -810,12 +819,12 @@ except Exception:
' "$SCRATCH/ord-$tag.json" 2>/dev/null || true) ' "$SCRATCH/ord-$tag.json" 2>/dev/null || true)
if [ -z "$OID" ]; then if [ -z "$OID" ]; then
if is_auth_kyc_body "$SCRATCH/ord-$tag.json"; then if is_auth_kyc_body "$SCRATCH/ord-$tag.json"; then
e2e_abort_auth "merchant-order" "merchant auth/KYC on pay $PAY_AMT" e2e_abort_auth "merchant-order" "merchant auth/KYC on pay $PAY_AMT ($PAY_SUM)"
fi fi
warn "pay $PAY_AMT" "order create failed — $(head -c 80 "$SCRATCH/ord-$tag.json" | tr '\n' ' ')" warn "pay $PAY_AMT ($PAY_SUM)" "order create failed — $(head -c 80 "$SCRATCH/ord-$tag.json" | tr '\n' ' ')"
return 1 return 1
fi fi
ok "merchant order $OID ($PAY_AMT)" ok "merchant order $OID ($PAY_AMT · $PAY_SUM)"
curl -skS -m 12 -o "$SCRATCH/ord-det-$tag.json" -H "$AUTH" \ curl -skS -m 12 -o "$SCRATCH/ord-det-$tag.json" -H "$AUTH" \
"${MERCHANT_PUBLIC}/instances/${INST}/private/orders/${OID}" 2>/dev/null || true "${MERCHANT_PUBLIC}/instances/${INST}/private/orders/${OID}" 2>/dev/null || true
PAYURI=$(python3 -c 'import json,sys PAYURI=$(python3 -c 'import json,sys
@ -826,15 +835,18 @@ except Exception: print("")
MH=$(python3 -c 'from urllib.parse import urlparse; print(urlparse("'"$MERCHANT_PUBLIC"'").hostname or "taler.hacktivism.ch")' 2>/dev/null || echo "taler.hacktivism.ch") MH=$(python3 -c 'from urllib.parse import urlparse; print(urlparse("'"$MERCHANT_PUBLIC"'").hostname or "taler.hacktivism.ch")' 2>/dev/null || echo "taler.hacktivism.ch")
PAYURI="taler://pay/${MH}/instances/${INST}/${OID}/?c=${OTOK}" PAYURI="taler://pay/${MH}/instances/${INST}/${OID}/?c=${OTOK}"
fi fi
# normalize default HTTPS port (wallets accept both)
PAYURI=$(printf '%s' "$PAYURI" | sed 's/:443\//\//g; s/:443?/?/g')
if [ -z "$PAYURI" ]; then if [ -z "$PAYURI" ]; then
warn "pay $PAY_AMT" "no pay URI for $OID" warn "pay $PAY_AMT ($PAY_SUM)" "no pay URI for $OID"
return 1 return 1
fi fi
ok "taler_pay_uri ready ($tag)"
if ! wcli_pay handle-uri --yes "$PAYURI" >"$SCRATCH/pay-$tag.out" 2>&1; then if ! wcli_pay handle-uri --yes "$PAYURI" >"$SCRATCH/pay-$tag.out" 2>&1; then
warn "pay $PAY_AMT" "handle-uri failed — $(tail -c 100 "$SCRATCH/pay-$tag.out" | tr '\n' ' ')" warn "pay $PAY_AMT ($PAY_SUM)" "handle-uri failed — $(tail -c 100 "$SCRATCH/pay-$tag.out" | tr '\n' ' ')"
return 1 return 1
fi fi
ok "wallet handle pay $PAY_AMT" ok "wallet handle pay $PAY_AMT ($PAY_SUM)"
local r local r
for r in 1 2 3; do for r in 1 2 3; do
wcli_pay run-until-done >"$SCRATCH/pay-run-$tag.out" 2>&1 || true wcli_pay run-until-done >"$SCRATCH/pay-run-$tag.out" 2>&1 || true
@ -847,11 +859,130 @@ except Exception: print("")
d=json.load(open(sys.argv[1])) d=json.load(open(sys.argv[1]))
sys.exit(0 if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid" else 1) sys.exit(0 if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid" else 1)
' "$SCRATCH/ord-paid-$tag.json" 2>/dev/null; then ' "$SCRATCH/ord-paid-$tag.json" 2>/dev/null; then
ok "payment settled $PAY_AMT (order $OID)" ok "payment settled $PAY_AMT ($PAY_SUM · order $OID)"
return 0 return 0
fi fi
done done
warn "pay $PAY_AMT" "not settled for order $OID" warn "pay $PAY_AMT ($PAY_SUM)" "not settled for order $OID"
return 1
}
# GOA shop samples from merchant landing (taler.hacktivism.ch/intro/)
# id|product_name|amount — keep in sync with configs/merchant-landing/index.html
# Landing QR encodes taler://pay-template/…/{id}; popup creates live pay via public POST templates/{id}.
: "${E2E_SHOP_PRODUCTS:=orbit-sticker|Orbit sticker pack|GOA:2
nebula-coffee|Nebula coffee|GOA:5
voidwave-playlist|Voidwave playlist|GOA:8
comet-cap|Comet cap|GOA:15
shuttle-pass|Shuttle day pass|GOA:25
beacon-badge|Beacon badge|GOA:3
relay-pin|Relay pin|GOA:4
eclipse-shades|Eclipse shades|GOA:12
star-chart|Star chart print|GOA:7
rainbow-pill|Rainbow pill (sample/joke)|GOA:4.2
blue-or-red-pill|Blue or red pill (sample/joke)|GOA:1.5}"
# Settlement payto shown on landing shop popup (static)
: "${E2E_SHOP_PAYTO:=payto://x-taler-bank/bank.hacktivism.ch/goa-demo-cp4zqk?receiver-name=GOA%20Demo%20Shop%20cp4zqk}"
# Pay one shop product the way the public landing does:
# POST /instances/{inst}/templates/{id} body {} → order_id+token → taler_pay_uri
# Also checks pay-template URI shape (what qrencode PNGs encode).
# Usage: e2e_one_pay_public_template PRODUCT_ID PRODUCT_NAME AMOUNT
e2e_one_pay_public_template() {
local pid="$1"
local pname="${2:-$1}"
local pamt="$3"
local tag
tag=$(printf 'shop_%s' "$pid" | tr -c 'A-Za-z0-9._-' '_')
section "e2e · shop product · $pname ($pid) · $pamt"
e2e_over && { warn "shop $pname" "budget exhausted — skip"; return 1; }
local MH
MH=$(python3 -c 'from urllib.parse import urlparse; print(urlparse("'"$MERCHANT_PUBLIC"'").hostname or "taler.hacktivism.ch")' 2>/dev/null || echo "taler.hacktivism.ch")
local TPL_URI="taler://pay-template/${MH}/instances/${INST}/${pid}"
local TPL_HTTPS="${MERCHANT_PUBLIC}/instances/${INST}/templates/$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "$pid")"
info "shop $pname" "pay-template URI $TPL_URI"
info "shop $pname" "public template POST $TPL_HTTPS"
info "shop $pname" "settlement payto $E2E_SHOP_PAYTO"
# Public create (no merchant secret) — same as shop-pay.js
curl -skS -m 15 -o "$SCRATCH/tpl-$tag.json" -X POST \
-H 'Content-Type: application/json' \
-d '{}' \
"$TPL_HTTPS" 2>"$SCRATCH/tpl-$tag.err" || true
if ! grep -q order_id "$SCRATCH/tpl-$tag.json" 2>/dev/null; then
if [ "$E2E_REMOTE" != "1" ] && koopa_ssh_ok; then
koopa_ssh_run 20 \
"curl -skS -m 12 -X POST -H 'Content-Type: application/json' -d '{}' 'https://127.0.0.1:9010/instances/${INST}/templates/${pid}'" \
>"$SCRATCH/tpl-$tag.json" 2>"$SCRATCH/tpl-$tag.err" || true
fi
fi
OID=$(python3 -c 'import json,re,sys;t=open(sys.argv[1]).read()
try: print(json.loads(t).get("order_id") or "")
except Exception:
m=re.search(r"\"order_id\"\s*:\s*\"([^\"]+)\"",t); print(m.group(1) if m else "")
' "$SCRATCH/tpl-$tag.json" 2>/dev/null || true)
OTOK=$(python3 -c 'import json,re,sys;t=open(sys.argv[1]).read()
try: print(json.loads(t).get("token") or "")
except Exception:
m=re.search(r"\"token\"\s*:\s*\"([^\"]+)\"",t); print(m.group(1) if m else "")
' "$SCRATCH/tpl-$tag.json" 2>/dev/null || true)
if [ -z "$OID" ] || [ -z "$OTOK" ]; then
warn "shop $pname ($pid)" "public template POST failed — $(head -c 100 "$SCRATCH/tpl-$tag.json" 2>/dev/null | tr '\n' ' ')"
return 1
fi
ok "shop $pname" "public order $OID (template $pid)"
local statusUrl="${MERCHANT_PUBLIC}/instances/${INST}/orders/$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "$OID")?token=$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "$OTOK")"
curl -skS -m 12 -o "$SCRATCH/tpl-st-$tag.json" "$statusUrl" 2>/dev/null || true
PAYURI=$(python3 -c 'import json,sys
try:
d=json.load(open(sys.argv[1]))
print(d.get("taler_pay_uri") or "")
except Exception: print("")
' "$SCRATCH/tpl-st-$tag.json" 2>/dev/null || true)
if [ -z "$PAYURI" ]; then
PAYURI="taler://pay/${MH}/instances/${INST}/${OID}/?c=${OTOK}"
fi
PAYURI=$(printf '%s' "$PAYURI" | sed 's/:443\//\//g; s/:443?/?/g')
if ! printf '%s' "$PAYURI" | grep -q '^taler://pay/'; then
warn "shop $pname ($pid)" "bad pay URI: $PAYURI"
return 1
fi
ok "shop $pname" "taler_pay_uri $PAYURI"
if ! wcli_pay handle-uri --yes "$PAYURI" >"$SCRATCH/pay-$tag.out" 2>&1; then
warn "shop $pname ($pid)" "handle-uri failed — $(tail -c 120 "$SCRATCH/pay-$tag.out" | tr '\n' ' ')"
return 1
fi
ok "shop $pname" "wallet accepted pay URI"
local r AUTH=""
if [ -n "${MPW:-}" ]; then
AUTH="Authorization: Bearer secret-token:${MPW}"
fi
for r in 1 2 3; do
wcli_pay run-until-done >"$SCRATCH/pay-run-$tag.out" 2>&1 || true
wcli_pay transactions >"$SCRATCH/tx-$tag.out" 2>&1 || true
if [ -n "$AUTH" ]; then
curl -skS -m 8 -o "$SCRATCH/ord-paid-$tag.json" -H "$AUTH" \
"${MERCHANT_PUBLIC}/instances/${INST}/private/orders/${OID}" 2>/dev/null || true
if python3 -c 'import json,sys
d=json.load(open(sys.argv[1]))
sys.exit(0 if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid" else 1)
' "$SCRATCH/ord-paid-$tag.json" 2>/dev/null; then
ok "shop $pname" "payment settled ($pamt · order $OID)"
return 0
fi
fi
if grep -qiE 'payment|paid|Payment' "$SCRATCH/tx-$tag.out" 2>/dev/null \
|| grep -qiE 'done|paid|success|Payment' "$SCRATCH/pay-$tag.out" 2>/dev/null; then
ok "shop $pname" "payment settled via wallet tx ($pamt · order $OID)"
return 0
fi
done
warn "shop $pname ($pid)" "not settled for order $OID"
return 1 return 1
} }
@ -974,10 +1105,65 @@ if [ "$PAY_OK" != "1" ]; then
fi fi
fi fi
# ---------------------------------------------------------------------------
section "e2e · GOA shop products (merchant landing catalog)"
# ---------------------------------------------------------------------------
# Public template POST + wallet pay — product id|name|amount (landing QR = pay-template).
SHOP_OK=0
SHOP_OK_N=0
SHOP_FAIL_N=0
SHOP_SKIP_N=0
SHOP_REPORT=""
if [ "$E2E_REMOTE" = "1" ] || [ "${CUR:-}" != "GOA" ]; then
info "goa-shop" "SKIPPED (remote or non-GOA currency)"
else
info "goa-shop" "public template pays for each sample (id · product name · amount)"
info "goa-shop payto" "$E2E_SHOP_PAYTO"
while IFS= read -r line; do
[ -z "$line" ] && continue
case "$line" in \#*) continue ;; esac
e2e_over && { warn "goa-shop" "time budget low — stopping product pays"; break; }
pid=$(printf '%s' "$line" | cut -d'|' -f1)
pname=$(printf '%s' "$line" | cut -d'|' -f2)
pamt=$(printf '%s' "$line" | cut -d'|' -f3)
[ -z "$pid" ] || [ -z "$pamt" ] && continue
[ -z "$pname" ] && pname="$pid"
av_now=$(wallet_avail_num)
pay_n=$(python3 -c 'import sys; print(float(sys.argv[1].split(":",1)[-1]))' "$pamt" 2>/dev/null || echo 0)
if ! python3 -c "import sys; sys.exit(0 if float(sys.argv[1]) + 1e-12 >= float(sys.argv[2]) else 1)" "$av_now" "$pay_n" 2>/dev/null; then
warn "goa-shop $pname ($pid)" "skip — insufficient avail ${CUR}:${av_now} (need ${pay_n})"
SHOP_SKIP_N=$((SHOP_SKIP_N + 1))
SHOP_REPORT="${SHOP_REPORT}${SHOP_REPORT:+ }${pname}=SKIP(bal)"
continue
fi
set +e
e2e_one_pay_public_template "$pid" "$pname" "$pamt"
pc=$?
set -e
if [ "$pc" = "0" ]; then
SHOP_OK=1
SHOP_OK_N=$((SHOP_OK_N + 1))
PAY_OK=1
PAY_OK_N=$((PAY_OK_N + 1))
SHOP_REPORT="${SHOP_REPORT}${SHOP_REPORT:+ }${pname}=OK"
else
SHOP_FAIL_N=$((SHOP_FAIL_N + 1))
SHOP_REPORT="${SHOP_REPORT}${SHOP_REPORT:+ }${pname}=FAIL"
fi
done <<EOF
$E2E_SHOP_PRODUCTS
EOF
info "goa-shop summary" "$SHOP_REPORT (ok=$SHOP_OK_N fail=$SHOP_FAIL_N skip=$SHOP_SKIP_N)"
if [ "$SHOP_OK" != "1" ] && [ "$SHOP_FAIL_N" -gt 0 ]; then
warn "goa-shop" "no sample product payment succeeded ($SHOP_REPORT)"
fi
fi
section "e2e · report" section "e2e · report"
info "user" "$USER" info "user" "$USER"
info "ATM withdraws" "$WITHDRAW_REPORT" info "ATM withdraws" "$WITHDRAW_REPORT"
info "payments" "$PAY_REPORT" info "payments" "$PAY_REPORT"
info "goa-shop" "${SHOP_REPORT:-(n/a)}"
info "final balance" "$(fmt_bal "$SCRATCH/bal-live.out" 2>/dev/null || echo "(n/a)")" info "final balance" "$(fmt_bal "$SCRATCH/bal-live.out" 2>/dev/null || echo "(n/a)")"
info "scratch" "$SCRATCH" info "scratch" "$SCRATCH"
# Success if we had coins and at least one pay, OR coins + only pay skips (nothing affordable) # Success if we had coins and at least one pay, OR coins + only pay skips (nothing affordable)