monitoring: e2e without run-until-done (wallet serve + poll)
This commit is contained in:
parent
d2113e7362
commit
62d0e426f4
2 changed files with 271 additions and 68 deletions
|
|
@ -23,19 +23,37 @@ e2e_over() { [ "$(e2e_left)" -le 0 ]; }
|
|||
# stdout only for JSON; logs go to stderr file so balance parse stays clean
|
||||
wcli_bal_snap() {
|
||||
local out="$1"
|
||||
if [ -z "${WALLET_CLI:-}" ] || [ ! -f "${WDB:-}" ]; then
|
||||
if [ -z "${WALLET_CLI:-}" ]; then
|
||||
echo "(no wallet)" >"$out"
|
||||
return 1
|
||||
fi
|
||||
# one-shot needs DB file; serve mode needs socket
|
||||
if [ -z "${WSERVE_PID:-}" ] || [ ! -S "${WSOCK:-}" ]; then
|
||||
if [ ! -f "${WDB:-}" ]; then
|
||||
echo "(no wallet)" >"$out"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
local err="${out}.err"
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
if [ -n "${WSERVE_PID:-}" ] && [ -S "${WSOCK:-}" ]; then
|
||||
perl -e 'alarm shift; exec @ARGV' 10 \
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle balance \
|
||||
>"$out" 2>"$err" || true
|
||||
else
|
||||
perl -e 'alarm shift; exec @ARGV' 10 \
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults balance \
|
||||
>"$out" 2>"$err" || true
|
||||
fi
|
||||
else
|
||||
if [ -n "${WSERVE_PID:-}" ] && [ -S "${WSOCK:-}" ]; then
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle balance \
|
||||
>"$out" 2>"$err" || true
|
||||
else
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults balance \
|
||||
>"$out" 2>"$err" || true
|
||||
fi
|
||||
fi
|
||||
[ -s "$out" ]
|
||||
}
|
||||
|
||||
|
|
@ -104,9 +122,9 @@ wait_wallet_balance() {
|
|||
local rounds="${2:-15}"
|
||||
local sleep_s="${3:-3}"
|
||||
local r av
|
||||
info "wallet settle wait" "up to ${rounds}×${sleep_s}s for avail>${min_n} ${CUR} (wirewatch lag is common)"
|
||||
# Poll balance only — never run-until-done (hangs; banned in monitoring).
|
||||
info "wallet settle wait" "up to ${rounds}×${sleep_s}s for avail>${min_n} ${CUR} (poll balance only; no run-until-done)"
|
||||
for r in $(seq 1 "$rounds"); do
|
||||
wcli run-until-done >"$SCRATCH/settle-run.out" 2>&1 || true
|
||||
wcli_bal_snap "$SCRATCH/bal-live.out" || wcli balance >"$SCRATCH/bal-live.out" 2>&1 || true
|
||||
av=$(wallet_avail_num "$SCRATCH/bal-live.out")
|
||||
if python3 -c "import sys; sys.exit(0 if float(sys.argv[1]) > float(sys.argv[2]) else 1)" "$av" "$min_n" 2>/dev/null; then
|
||||
|
|
@ -221,12 +239,22 @@ except Exception: print("")' 2>/dev/null || true)
|
|||
if [ "$E2E_REMOTE" = "1" ]; then
|
||||
# remote: still ATM-shaped but smaller notes; keep cheap
|
||||
: "${E2E_WITHDRAW_VALUES:=10 20 50}"
|
||||
if [ "$CUR" = "TESTPAYSAN" ]; then
|
||||
# match fixed public templates e2e-001 / e2e-005 / e2e-1
|
||||
: "${E2E_PAY_VALUES:=0.01 0.05 1}"
|
||||
else
|
||||
: "${E2E_PAY_VALUES:=0.01 0.05 0.1 1}"
|
||||
fi
|
||||
else
|
||||
# local GOA: classic ATM notes + 4200 for paivana paywall template
|
||||
: "${E2E_WITHDRAW_VALUES:=20 50 100 200 4200}"
|
||||
: "${E2E_PAY_VALUES:=0.01 0.05 0.1 0.5 1 2 5 10}"
|
||||
fi
|
||||
# Public template pays (landing/shop style) — default on for TESTPAYSAN stage
|
||||
if [ -z "${E2E_USE_TEMPLATES:-}" ]; then
|
||||
if [ "$CUR" = "TESTPAYSAN" ]; then E2E_USE_TEMPLATES=1; else E2E_USE_TEMPLATES=0; fi
|
||||
fi
|
||||
: "${E2E_USE_TEMPLATES:=0}"
|
||||
|
||||
# Build CUR:amount lists
|
||||
build_amt_list() {
|
||||
|
|
@ -300,12 +328,27 @@ BANK_HOST=$(python3 -c 'from urllib.parse import urlparse; print(urlparse("'"$BA
|
|||
|
||||
SCRATCH=$(mktemp -d)
|
||||
WDB="$SCRATCH/wallet.sqlite3"
|
||||
WSOCK="$SCRATCH/wallet.sock"
|
||||
WSERVE_PID=""
|
||||
# Client/server wallet (advanced serve): long-lived shepherd without run-until-done.
|
||||
# Default on — wallet-cli only completes withdraw/pay while a serve process is up.
|
||||
: "${E2E_WALLET_SERVE:=1}"
|
||||
METRICS_DIR="${METRICS_DIR:-$SCRATCH/metrics}"
|
||||
mkdir -p "$METRICS_DIR"
|
||||
export METRICS_DIR CUR="${CUR:-${EXPECT_CURRENCY:-GOA}}" WDB
|
||||
export PATH="${HOME}/.local/bin:${HOME}/taler/opt/taler-wallet-cli/usr/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
||||
|
||||
stop_wallet_serve() {
|
||||
if [ -n "${WSERVE_PID:-}" ] && kill -0 "$WSERVE_PID" 2>/dev/null; then
|
||||
kill "$WSERVE_PID" 2>/dev/null || true
|
||||
wait "$WSERVE_PID" 2>/dev/null || true
|
||||
fi
|
||||
WSERVE_PID=""
|
||||
rm -f "${WSOCK:-}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Always dump balances on any exit (timeout, blocker, signal, success)
|
||||
trap 'ec=$?; e2e_finish "$ec"; rm -rf "$SCRATCH"; exit "$ec"' EXIT
|
||||
trap 'ec=$?; e2e_finish "$ec"; stop_wallet_serve; rm -rf "$SCRATCH"; exit "$ec"' EXIT
|
||||
|
||||
# Abort cleanly on login / KYC / registration barriers (esp. remote domains)
|
||||
e2e_abort_auth() {
|
||||
|
|
@ -405,13 +448,29 @@ else
|
|||
exit 1
|
||||
fi
|
||||
if [ -n "$MPW" ]; then
|
||||
# Secrets often store full "secret-token:…"; strip before re-prefixing Bearer.
|
||||
MPW=$(printf '%s' "$MPW" | tr -d '\n\r')
|
||||
case "$MPW" in
|
||||
secret-token:*) MPW_TOKEN="$MPW" ;;
|
||||
*) MPW_TOKEN="secret-token:${MPW}" ;;
|
||||
esac
|
||||
ok "merchant secret (instance ${MERCHANT_INSTANCE})"
|
||||
elif [ "$E2E_REMOTE" = "1" ]; then
|
||||
warn "merchant secret" "missing — order create may fail (set E2E_MERCHANT_TOKEN)"
|
||||
MPW_TOKEN=""
|
||||
warn "merchant secret" "missing — private orders fail unless E2E_USE_TEMPLATES=1 (set E2E_MERCHANT_TOKEN)"
|
||||
else
|
||||
blocker "prereq" "merchant instance password missing"
|
||||
exit 1
|
||||
fi
|
||||
# Authorization header for merchant private API (never double secret-token:)
|
||||
merchant_auth_header() {
|
||||
local t="${1:-${MPW_TOKEN:-}}"
|
||||
[ -n "$t" ] || return 1
|
||||
case "$t" in
|
||||
secret-token:*) printf 'Authorization: Bearer %s' "$t" ;;
|
||||
*) printf 'Authorization: Bearer secret-token:%s' "$t" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Public reachability gates (remote: soft-skip if bank/merchant absent)
|
||||
for pair in \
|
||||
|
|
@ -455,7 +514,31 @@ info "e2e user" "$USER withdraw=$WITHDRAW_AMT pay=$PAY_AMT"
|
|||
BANK="$BANK_PUBLIC"
|
||||
INST="$MERCHANT_INSTANCE"
|
||||
|
||||
start_wallet_serve() {
|
||||
[ "${E2E_WALLET_SERVE}" = "1" ] || return 0
|
||||
[ -n "${WALLET_CLI:-}" ] && [ -f "${WALLET_CLI}" ] || return 1
|
||||
stop_wallet_serve
|
||||
rm -f "$WSOCK"
|
||||
# serve holds the DB open; clients use --wallet-connection only
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults \
|
||||
advanced serve --unix-path "$WSOCK" \
|
||||
>"$SCRATCH/wallet-serve.log" 2>&1 &
|
||||
WSERVE_PID=$!
|
||||
local i
|
||||
for i in $(seq 1 40); do
|
||||
if [ -S "$WSOCK" ] && kill -0 "$WSERVE_PID" 2>/dev/null; then
|
||||
ok "wallet serve" "pid=$WSERVE_PID sock=$WSOCK (no run-until-done)"
|
||||
return 0
|
||||
fi
|
||||
sleep 0.15
|
||||
done
|
||||
warn "wallet serve" "socket not ready — falling back to one-shot CLI (coins may stay pending)"
|
||||
stop_wallet_serve
|
||||
return 1
|
||||
}
|
||||
|
||||
# $1 = max seconds for this call (optional); rest = wallet-cli args
|
||||
# Prefer live serve socket so the shepherd stays up (alternative to run-until-done).
|
||||
wcli() {
|
||||
local maxc=12
|
||||
if [[ "${1:-}" =~ ^[0-9]+$ ]]; then
|
||||
|
|
@ -467,12 +550,21 @@ wcli() {
|
|||
cap=$(e2e_left)
|
||||
[ "$cap" -gt "$maxc" ] && cap=$maxc
|
||||
[ "$cap" -lt 3 ] && return 124
|
||||
if [ -n "${WSERVE_PID:-}" ] && [ -S "${WSOCK:-}" ]; then
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -e 'alarm shift; exec @ARGV' "$cap" \
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle "$@"
|
||||
else
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle "$@"
|
||||
fi
|
||||
else
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -e 'alarm shift; exec @ARGV' "$cap" \
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"
|
||||
else
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Pay must not be starved: use reserved seconds even if global budget is tight
|
||||
|
|
@ -489,12 +581,21 @@ wcli_pay() {
|
|||
cap=12
|
||||
fi
|
||||
info "pay wallet cap" "${cap}s"
|
||||
if [ -n "${WSERVE_PID:-}" ] && [ -S "${WSOCK:-}" ]; then
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -e 'alarm shift; exec @ARGV' "$cap" \
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle "$@"
|
||||
else
|
||||
node "$WALLET_CLI" --wallet-connection="$WSOCK" --no-throttle "$@"
|
||||
fi
|
||||
else
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -e 'alarm shift; exec @ARGV' "$cap" \
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"
|
||||
else
|
||||
node "$WALLET_CLI" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# When coins never become available: run inside + targeted bank/exchange dig
|
||||
|
|
@ -686,6 +787,8 @@ fi
|
|||
set_group wallet
|
||||
section "e2e · wallet setup (exchange + ToS once)"
|
||||
# ---------------------------------------------------------------------------
|
||||
# Long-lived serve = client/server wallet (shepherd keeps running; no run-until-done).
|
||||
start_wallet_serve || true
|
||||
if ! wcli exchanges add "${EXCHANGE_PUBLIC}/" >"$SCRATCH/ex-add.out" 2>&1; then
|
||||
if ! grep -qiE 'already|ok' "$SCRATCH/ex-add.out"; then
|
||||
blocker "wallet-exchange" "exchanges add failed — $(tail -c 160 "$SCRATCH/ex-add.out" | tr '\n' ' ')"
|
||||
|
|
@ -780,12 +883,13 @@ e2e_one_withdraw() {
|
|||
fi
|
||||
cp "$SCRATCH/accept-$tag.out" "$SCRATCH/accept.out"
|
||||
|
||||
# Poll bank withdrawal status only (no run-until-done). force-select if stuck pending.
|
||||
st=""
|
||||
for i in 1 2 3 4; do
|
||||
for i in 1 2 3 4 5 6 8 10; do
|
||||
e2e_over && break
|
||||
wcli run-until-done >"$SCRATCH/sel-$tag-$i.out" 2>&1 || true
|
||||
st=$(wd_status)
|
||||
case "$st" in selected|confirmed|aborted) break ;; esac
|
||||
sleep 0.4
|
||||
done
|
||||
if [ "$st" != "selected" ] && [ "$st" != "confirmed" ]; then
|
||||
wcli transactions >"$SCRATCH/tx-pre.json" 2>&1 || true
|
||||
|
|
@ -830,10 +934,9 @@ else:
|
|||
fi
|
||||
fake_incoming_speedup
|
||||
|
||||
# Short per-ATM poll; full settle wait happens after the ladder (avoids false FAIL)
|
||||
local ok_bal=0 r av
|
||||
for r in 1 2 3 4 5 6; do
|
||||
wcli run-until-done >"$SCRATCH/run-$tag-$r.out" 2>&1 || true
|
||||
# Short per-ATM settle: poll balance + bank transfer_done only (never run-until-done)
|
||||
local ok_bal=0 r av xfer
|
||||
for r in 1 2 3 4 5 6 8 10; do
|
||||
wcli_bal_snap "$SCRATCH/bal-$tag.out" || wcli balance >"$SCRATCH/bal-$tag.out" 2>&1 || true
|
||||
av=$(wallet_avail_num "$SCRATCH/bal-$tag.out")
|
||||
if python3 -c "import sys; sys.exit(0 if float(sys.argv[1]) > 0 else 1)" "$av" 2>/dev/null; then
|
||||
|
|
@ -842,6 +945,13 @@ else:
|
|||
info "balance" "$(fmt_bal "$SCRATCH/bal-$tag.out")"
|
||||
break
|
||||
fi
|
||||
xfer=$(curl -sS -m 8 "$BANK/taler-integration/withdrawal-operation/${WID}" 2>/dev/null \
|
||||
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("transfer_done"), d.get("status"))' 2>/dev/null || echo "?")
|
||||
if echo "$xfer" | grep -qi True; then
|
||||
ok "ATM $WITHDRAW_AMT bank transfer_done" "wallet avail=${CUR}:${av} ($xfer) — no run-until-done"
|
||||
ok_bal=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
metrics_report_coins "after-ATM-${tag}" || true
|
||||
|
|
@ -859,8 +969,28 @@ else:
|
|||
return 1
|
||||
}
|
||||
|
||||
# Map CUR:amount → public template id (TESTPAYSAN stage e2e-* templates).
|
||||
# Override with E2E_TEMPLATE_MAP="0.01=e2e-001 0.05=e2e-005 1=e2e-1"
|
||||
e2e_template_id_for_amount() {
|
||||
local amt="$1"
|
||||
local val map_entry k v
|
||||
val=$(printf '%s' "$amt" | awk -F: '{print $NF}')
|
||||
: "${E2E_TEMPLATE_MAP:=0.01=e2e-001 0.05=e2e-005 1=e2e-1 1.0=e2e-1}"
|
||||
for map_entry in $E2E_TEMPLATE_MAP; do
|
||||
k="${map_entry%%=*}"
|
||||
v="${map_entry#*=}"
|
||||
if [ "$k" = "$val" ]; then
|
||||
printf '%s' "$v"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# One payment: e2e_one_pay AMOUNT [SUMMARY] [TAG]
|
||||
# SUMMARY defaults to "monitoring pay $AMOUNT"; TAG defaults from amount.
|
||||
# When E2E_USE_TEMPLATES=1 and amount maps to a public template, uses
|
||||
# POST /instances/{inst}/templates/{id} (same as landings/shops).
|
||||
e2e_one_pay() {
|
||||
PAY_AMT="$1"
|
||||
local PAY_SUM="${2:-monitoring pay ${PAY_AMT}}"
|
||||
|
|
@ -873,11 +1003,28 @@ e2e_one_pay() {
|
|||
section "e2e · pay $PAY_AMT · $(format_amount_alt "$PAY_AMT") · $PAY_SUM"
|
||||
e2e_over && { warn "pay" "budget exhausted — skip $PAY_AMT ($PAY_SUM)"; return 1; }
|
||||
metrics_report_coins "before-pay-${tag}" || true
|
||||
if [ -z "${MPW:-}" ]; then
|
||||
|
||||
# Prefer public templates when enabled (stage TESTPAYSAN / shop-style orders)
|
||||
local tpl_id=""
|
||||
if [ "${E2E_USE_TEMPLATES:-0}" = "1" ]; then
|
||||
tpl_id=$(e2e_template_id_for_amount "$PAY_AMT" 2>/dev/null || true)
|
||||
if [ -n "$tpl_id" ]; then
|
||||
info "pay $PAY_AMT" "via public template $tpl_id (E2E_USE_TEMPLATES=1)"
|
||||
e2e_one_pay_public_template "$tpl_id" "$PAY_SUM" "$PAY_AMT"
|
||||
return $?
|
||||
fi
|
||||
warn "pay $PAY_AMT" "no template map for amount — fall back to private order"
|
||||
fi
|
||||
|
||||
if [ -z "${MPW:-}" ] && [ -z "${MPW_TOKEN:-}" ]; then
|
||||
warn "pay $PAY_AMT" "no merchant token"
|
||||
return 1
|
||||
fi
|
||||
AUTH="Authorization: Bearer secret-token:${MPW}"
|
||||
local AUTH
|
||||
AUTH=$(merchant_auth_header) || {
|
||||
warn "pay $PAY_AMT" "no merchant token"
|
||||
return 1
|
||||
}
|
||||
# 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")
|
||||
|
|
@ -887,8 +1034,9 @@ e2e_one_pay() {
|
|||
"${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 [ "$E2E_REMOTE" != "1" ] && koopa_ssh_ok; then
|
||||
local mpw_raw="${MPW_TOKEN:-secret-token:${MPW}}"
|
||||
koopa_ssh_run 20 \
|
||||
"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'" \
|
||||
"curl -skS -m 12 -X POST -H 'Authorization: Bearer ${mpw_raw}' -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
|
||||
fi
|
||||
fi
|
||||
|
|
@ -932,23 +1080,37 @@ except Exception: print("")
|
|||
return 1
|
||||
fi
|
||||
ok "wallet handle pay $PAY_AMT ($PAY_SUM)"
|
||||
# Settle: poll merchant order + wallet transactions only (never run-until-done)
|
||||
local r
|
||||
for r in 1 2 3; do
|
||||
wcli_pay run-until-done >"$SCRATCH/pay-run-$tag.out" 2>&1 || true
|
||||
for r in 1 2 3 4 5 6; do
|
||||
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
|
||||
fi
|
||||
if [ -n "${OTOK:-}" ]; then
|
||||
curl -skS -m 8 -o "$SCRATCH/ord-pub-$tag.json" \
|
||||
"${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")" \
|
||||
2>/dev/null || true
|
||||
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 \
|
||||
|| 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
|
||||
for p in sys.argv[1:]:
|
||||
try:
|
||||
d=json.load(open(p))
|
||||
if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid":
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
' "$SCRATCH/ord-paid-$tag.json" "$SCRATCH/ord-pub-$tag.json" 2>/dev/null; then
|
||||
ok "payment settled $PAY_AMT ($PAY_SUM · order $OID)"
|
||||
metrics_report_coins "after-pay-${tag}" || true
|
||||
metrics_record_flow spent "$PAY_AMT" || true
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
metrics_report_coins "after-pay-fail-${tag}" || true
|
||||
warn "pay $PAY_AMT ($PAY_SUM)" "not settled for order $OID"
|
||||
|
|
@ -1051,33 +1213,34 @@ except Exception: print("")
|
|||
fi
|
||||
ok "shop $pname" "wallet accepted pay URI"
|
||||
|
||||
# Settle: public order status + transactions only (never run-until-done)
|
||||
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
|
||||
AUTH=$(merchant_auth_header 2>/dev/null) || AUTH=""
|
||||
for r in 1 2 3 4 5 6; do
|
||||
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
|
||||
fi
|
||||
curl -skS -m 8 -o "$SCRATCH/ord-pub-$tag.json" "$statusUrl" 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
|
||||
for p in sys.argv[1:]:
|
||||
try:
|
||||
d=json.load(open(p))
|
||||
if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid":
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
' "$SCRATCH/ord-paid-$tag.json" "$SCRATCH/ord-pub-$tag.json" 2>/dev/null \
|
||||
|| 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 ($pamt · order $OID)"
|
||||
metrics_report_coins "after-shop-${tag}" || true
|
||||
metrics_record_flow spent "$pamt" || true
|
||||
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)"
|
||||
metrics_report_coins "after-shop-${tag}" || true
|
||||
metrics_record_flow spent "$pamt" || true
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
metrics_report_coins "after-shop-fail-${tag}" || true
|
||||
warn "shop $pname ($pid)" "not settled for order $OID"
|
||||
|
|
@ -1171,13 +1334,16 @@ PAY_OK_N=0
|
|||
PAY_FAIL_N=0
|
||||
PAY_SKIP_N=0
|
||||
PAY_REPORT=""
|
||||
if [ -z "${MPW:-}" ]; then
|
||||
if [ -z "${MPW:-}" ] && [ "${E2E_USE_TEMPLATES:-0}" != "1" ]; then
|
||||
if [ "$E2E_REMOTE" = "1" ]; then
|
||||
e2e_abort_auth "merchant-order" "no merchant token — set E2E_MERCHANT_TOKEN"
|
||||
e2e_abort_auth "merchant-order" "no merchant token — set E2E_MERCHANT_TOKEN (or E2E_USE_TEMPLATES=1)"
|
||||
fi
|
||||
blocker "merchant-order" "no merchant token"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${MPW:-}" ] && [ "${E2E_USE_TEMPLATES:-0}" = "1" ]; then
|
||||
info "pay" "no merchant token — public templates only (E2E_USE_TEMPLATES=1)"
|
||||
fi
|
||||
|
||||
for PAY_AMT in $PAY_LIST; do
|
||||
e2e_over && { warn "pay ladder" "time budget low — stopping more payments"; break; }
|
||||
|
|
@ -1219,18 +1385,46 @@ fi
|
|||
|
||||
# ---------------------------------------------------------------------------
|
||||
set_group shop
|
||||
section "e2e · GOA shop products (merchant landing catalog)"
|
||||
section "e2e · shop products (public templates)"
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public template POST + wallet pay — full catalog list, random pick of N products.
|
||||
# Public template POST + wallet pay — catalog random pick of N products.
|
||||
# GOA: goa-shop on hacktivism. TESTPAYSAN: farmer shops (jardin / fermes).
|
||||
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)"
|
||||
# Auto catalog for stage TESTPAYSAN when not overridden
|
||||
if [ "${CUR:-}" = "TESTPAYSAN" ] && [ -z "${E2E_SHOP_PRODUCTS_SET:-}" ] && [ -z "${E2E_SHOP_FORCE_GOA:-}" ]; then
|
||||
# id|name|amount|instance — force stage catalog (GOA default is set earlier)
|
||||
E2E_SHOP_PRODUCTS="oeufs-6|Œufs plein air × 6|TESTPAYSAN:5|jardin-du-creux
|
||||
pain-seigle|Pain de seigle 800 g|TESTPAYSAN:4.5|jardin-du-creux
|
||||
jus-pomme|Jus de pomme 1 L|TESTPAYSAN:6|jardin-du-creux
|
||||
panier-legumes|Panier légumes|TESTPAYSAN:25|fermes-des-collines
|
||||
fromage-chevre|Fromage de chèvre 200 g|TESTPAYSAN:8.5|fermes-des-collines
|
||||
miel-printemps|Miel de printemps 250 g|TESTPAYSAN:12|fermes-des-collines"
|
||||
# Force stage defaults (GOA goa-shop is already set earlier via :=)
|
||||
if [ -z "${E2E_SHOP_INSTANCE_SET:-}" ]; then
|
||||
E2E_SHOP_INSTANCE=jardin-du-creux
|
||||
fi
|
||||
if [ -z "${E2E_SHOP_PAYTO_SET:-}" ]; then
|
||||
E2E_SHOP_PAYTO="payto://x-taler-bank/stage.bank.lefrancpaysan.ch/jardin-du-creux"
|
||||
fi
|
||||
E2E_SHOP_PICK_N="${E2E_SHOP_PICK_N:-2}"
|
||||
E2E_SHOP_ENABLE=1
|
||||
fi
|
||||
# GOA local only by default; remote non-GOA shops need E2E_SHOP_ENABLE=1 or TESTPAYSAN auto above
|
||||
if [ -z "${E2E_SHOP_ENABLE:-}" ]; then
|
||||
if [ "$E2E_REMOTE" = "1" ] || [ "${CUR:-}" != "GOA" ]; then
|
||||
E2E_SHOP_ENABLE=0
|
||||
else
|
||||
E2E_SHOP_ENABLE=1
|
||||
fi
|
||||
fi
|
||||
if [ "${E2E_SHOP_ENABLE}" != "1" ]; then
|
||||
info "shop" "SKIPPED (set E2E_SHOP_ENABLE=1 or use TESTPAYSAN stage catalog)"
|
||||
else
|
||||
# Build catalog array from E2E_SHOP_PRODUCTS (id|name|amount lines)
|
||||
# Build catalog array from E2E_SHOP_PRODUCTS (id|name|amount[|instance] lines)
|
||||
SHOP_CATALOG=()
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
|
|
@ -1244,7 +1438,7 @@ EOF
|
|||
case "$PICK_N" in ''|*[!0-9]*) PICK_N=2 ;; esac
|
||||
if [ "$PICK_N" -lt 1 ]; then PICK_N=1; fi
|
||||
if [ "$SHOP_CAT_N" -eq 0 ]; then
|
||||
warn "goa-shop" "empty E2E_SHOP_PRODUCTS catalog"
|
||||
warn "shop" "empty E2E_SHOP_PRODUCTS catalog"
|
||||
else
|
||||
if [ "$PICK_N" -gt "$SHOP_CAT_N" ]; then PICK_N=$SHOP_CAT_N; fi
|
||||
# Shuffle catalog, take first PICK_N (portable: python)
|
||||
|
|
@ -1257,24 +1451,26 @@ random.shuffle(lines)
|
|||
print('\n'.join(lines[:n]))
|
||||
" "$PICK_N"
|
||||
)
|
||||
info "goa-shop" "catalog ${SHOP_CAT_N} products · random pick ${PICK_N} · instance ${E2E_SHOP_INSTANCE}"
|
||||
info "goa-shop payto" "$E2E_SHOP_PAYTO"
|
||||
info "goa-shop pick" "$(printf '%s\n' "$SHOP_PICKED" | cut -d'|' -f1,2 | tr '\n' '; ' | sed 's/; $//')"
|
||||
# e2e_one_pay_public_template uses global INST — temporarily pin shop instance
|
||||
info "shop" "catalog ${SHOP_CAT_N} products · random pick ${PICK_N} · default instance ${E2E_SHOP_INSTANCE}"
|
||||
info "shop payto" "$E2E_SHOP_PAYTO"
|
||||
info "shop pick" "$(printf '%s\n' "$SHOP_PICKED" | cut -d'|' -f1,2 | tr '\n' '; ' | sed 's/; $//')"
|
||||
# e2e_one_pay_public_template uses global INST — pin per product if 4th field set
|
||||
_E2E_INST_SAVE="$INST"
|
||||
INST="${E2E_SHOP_INSTANCE}"
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
e2e_over && { warn "goa-shop" "time budget low — stopping product pays"; break; }
|
||||
e2e_over && { warn "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)
|
||||
pinst=$(printf '%s' "$line" | cut -d'|' -f4)
|
||||
[ -z "$pid" ] || [ -z "$pamt" ] && continue
|
||||
[ -z "$pname" ] && pname="$pid"
|
||||
INST="${pinst:-${E2E_SHOP_INSTANCE}}"
|
||||
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})"
|
||||
warn "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
|
||||
|
|
@ -1298,9 +1494,9 @@ $SHOP_PICKED
|
|||
EOF
|
||||
INST="$_E2E_INST_SAVE"
|
||||
unset _E2E_INST_SAVE
|
||||
info "goa-shop summary" "$SHOP_REPORT (ok=$SHOP_OK_N fail=$SHOP_FAIL_N skip=$SHOP_SKIP_N · pick $PICK_N/$SHOP_CAT_N)"
|
||||
info "shop summary" "$SHOP_REPORT (ok=$SHOP_OK_N fail=$SHOP_FAIL_N skip=$SHOP_SKIP_N · pick $PICK_N/$SHOP_CAT_N)"
|
||||
if [ "$SHOP_OK" != "1" ] && [ "$SHOP_FAIL_N" -gt 0 ]; then
|
||||
warn "goa-shop" "no sample product payment succeeded ($SHOP_REPORT)"
|
||||
warn "shop" "no sample product payment succeeded ($SHOP_REPORT)"
|
||||
fi
|
||||
section "e2e · load snapshot (after shop pays)"
|
||||
metrics_report_load "${METRICS_DIR}/load-after-shop.json" "after-shop" || true
|
||||
|
|
@ -1406,7 +1602,7 @@ section "e2e · report"
|
|||
info "user" "$USER"
|
||||
info "ATM withdraws" "$WITHDRAW_REPORT"
|
||||
info "payments" "$PAY_REPORT"
|
||||
info "goa-shop" "${SHOP_REPORT:-(n/a)}"
|
||||
info "shop" "${SHOP_REPORT:-(n/a)}"
|
||||
info "paivana" "${PAIVANA_REPORT:-(n/a)}"
|
||||
info "final balance" "$(fmt_bal "$SCRATCH/bal-live.out" 2>/dev/null || echo "(n/a)")"
|
||||
info "scratch" "$SCRATCH"
|
||||
|
|
|
|||
|
|
@ -193,6 +193,13 @@ apply_taler_domain() {
|
|||
PAY_AMT="${PAY_AMT:-${EXPECT_CURRENCY}:0.01}"
|
||||
CREDIT_AMT="${CREDIT_AMT:-${EXPECT_CURRENCY}:100}"
|
||||
;;
|
||||
TESTPAYSAN)
|
||||
# Stage FrancPaysan: default instance + public templates (e2e-001/005/1)
|
||||
MERCHANT_INSTANCE="${MERCHANT_INSTANCE:-default}"
|
||||
WITHDRAW_AMT="${WITHDRAW_AMT:-TESTPAYSAN:20}"
|
||||
PAY_AMT="${PAY_AMT:-TESTPAYSAN:0.01}"
|
||||
CREDIT_AMT="${CREDIT_AMT:-TESTPAYSAN:100}"
|
||||
;;
|
||||
CHF)
|
||||
WITHDRAW_AMT="${WITHDRAW_AMT:-CHF:20}"
|
||||
PAY_AMT="${PAY_AMT:-CHF:0.01}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue