Compare commits

..

1 commit

Author SHA1 Message Date
Hernâni Marques
bc785f8466
fix 1.26.1: public order settle uses StatusPaidResponse 2026-08-31 21:18:31 +02:00
6 changed files with 34 additions and 54 deletions

View file

@ -228,15 +228,15 @@ Sources: `https://bank.hacktivism.ch/intro/stats.json`, exchange/merchant intro
| **Workaround** | Create CHF:0 `stickers` template on stage.my (merchant token). Until then phase ERROR is correct. |
| **Wanted** | Same public template as prod on stage. |
### 9c. TESTPAYSAN token-pay: public order JSON has no `"paid":true`
### 9c. public GET paid = `StatusPaidResponse` (no `paid` / `order_status`)
| | |
|--|--|
| **Seen** | 2026-08-30 magikoopa e2e `mon09529`; OIDs `2026.242-0K4GJPET664XG`, `Y4T1SDEEZ5S3R`, `XWNC7P1VH21FP`; `token-pay paid1=FAIL paid2=FAIL free1=FAIL` while suite exit 0 |
| **Area** | **cli** / **ops** |
| **Problem** | After `handle-uri --yes --choice-index=0\|1`, `check_e2e.sh` settle polls the **public** merchant order JSON (`?token=`). That body often has `refunded:false` and **no** `"paid":true` / `"order_status":"paid"` during the poll window. Private `GET …/private/orders/OID` later shows `paid=true`; kiosque `issued=2 used=2`. Parser treats missing field as unpaid. |
| **Workaround** | Settle against **instance private** `order_status=paid` (kiosque token), not public JSON. Keep poll ≥20×2s. Do not trust `handle-uri` exit 0 alone (expired/failed still exit 0). |
| **Wanted** | Public order GET with `order_status` for the pay token; wallet-cli non-zero on Payment expired/failed. |
| **Seen** | TESTPAYSAN token-pay 2026-08-30/31; e2e WARN not settled; body `{refunded:false, refund_taken:…}` |
| **Area** | **ops** |
| **Problem** | Suite required `paid` / `order_status` on **public** GET. Spec does not (`get-orders-ORDER_ID.rst` `StatusPaidResponse`: refund_* only). `order_status:"paid"` is **private** (`CheckPaymentPaidResponse`). |
| **Workaround** | `merchant_order_json_is_paid`: refunded/refund_taken, no `taler_pay_uri`. Do not trust `handle-uri` exit 0. |
| **Wanted** | wallet-cli non-zero on Payment expired/failed. |
### 9d. Merchant `/keys` parse fail → 2010 then recover from DB

View file

@ -1 +1 @@
1.26.0
1.26.1

View file

@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre
| Tag | Date (UTC) | Notes |
|-----|------------|--------|
| **v1.26.1** | 2026-08-31 | **Bugfix:** public order settle accepts `StatusPaidResponse` (`refunded`/`refund_taken`); `paid`/`order_status` not on public GET. |
| **v1.26.0** | 2026-08-30 | **Feature:** `--wallet-db PATH` / `--wallet-migrate` (taler-wallet-cli / taler-harness names) for e2e, ladder, zero-pay. TESTPAYSAN: one Quadlet `stage-testpaysan`; e2e token-pay via env `E2E_TOKEN_*` / `--choice-index`. Stack READMEs `README_testpaysan.md` / `README_mytops.md`. |
| **v1.24.0** | 2026-08-30 | **Feature / zero-pay:** public-template pays as phase `zero-pay` (`check_zero_pay.sh`). Default merchant **stage.my.taler-ops.ch** (CHF:0 stickers). Wrapper `pay_talersticker_zero.sh` COUNT=100. TESTPAYSAN: empty POST `{}` + auto-confirm of bank wires (host `auto-confirm-withdrawals`). Not in default 4h PHASES (use COUNT=1). |
| **v1.23.9** | 2026-08-30 | **Bugfix / versions:** `INSIDE_PODMAN=1` on stagepaysan still used GOA container names → empty `dpkg-query`**versions.host-04** ERROR on `/monitoring_err`. Stage-lfp names apply in host-podman too; `run-fp-stage-monitoring.sh` exports `INSIDE_*_CTR`. |

View file

@ -1200,16 +1200,7 @@ except Exception: print("")
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
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
|| merchant_order_json_is_paid "$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
@ -1340,16 +1331,7 @@ except Exception: print("")
"${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
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 \
if merchant_order_json_is_paid "$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)"
@ -1773,22 +1755,7 @@ except Exception: print("")' "$create_f" 2>/dev/null || true)
curl -skS -m 8 -o "$SCRATCH/token-st-$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
if python3 -c 'import json,sys
try:
d=json.load(open(sys.argv[1]))
st=str(d.get("order_status") or d.get("status") or "").lower()
if d.get("paid") is True or st=="paid":
raise SystemExit(0)
except FileNotFoundError:
pass
except SystemExit:
raise
except Exception:
pass
raise SystemExit(1)
' "$SCRATCH/token-st-$tag.json" 2>/dev/null \
|| grep -qE '"paid"[[:space:]]*:[[:space:]]*true|"order_status"[[:space:]]*:[[:space:]]*"paid"' \
"$SCRATCH/token-st-$tag.json" 2>/dev/null; then
if merchant_order_json_is_paid "$SCRATCH/token-st-$tag.json" 2>/dev/null; then
ok "token-pay $tag" "paid choice=$choice oid=$oid"
return 0
fi

View file

@ -223,17 +223,7 @@ order_public() {
}
order_is_paid() {
python3 -c 'import json,sys
try:
d=json.load(open(sys.argv[1]))
except Exception:
raise SystemExit(1)
if d.get("paid") is True or str(d.get("order_status","")).lower()=="paid":
raise SystemExit(0)
if "refunded" in d or "refund_amount" in d:
raise SystemExit(0)
raise SystemExit(1)
' "$1"
merchant_order_json_is_paid "$1"
}
wallet_handle_done() {

22
lib.sh
View file

@ -22,6 +22,28 @@ taler_mon_resolve_wallet_db() {
printf '%s\n' "$p"
}
# Merchant order JSON is paid? Public GET: StatusPaidResponse (refunded / refund_taken).
# Spec does not put paid / order_status there (those are private CheckPaymentPaidResponse).
merchant_order_json_is_paid() {
python3 -c '
import json, sys
for p in sys.argv[1:]:
try:
d = json.load(open(p))
except Exception:
continue
if not isinstance(d, dict):
continue
if d.get("paid") is True or str(d.get("order_status") or "").lower() == "paid":
raise SystemExit(0)
if d.get("taler_pay_uri"):
continue
if "refunded" in d or "refund_amount" in d or "refund_taken" in d:
raise SystemExit(0)
raise SystemExit(1)
' "$@"
}
# Load host env once (before any := defaults)
if [ "${_TALER_MON_ENV_LOADED:-0}" != "1" ]; then
_mon_cfg="${TALER_MONITORING_ENV:-${XDG_CONFIG_HOME:-$HOME/.config}/taler-monitoring/env}"