fix(scripts): payment-e2e walkthrough safety guards (DRY_RUN + prod confirm)
Three holes in the v1.0.9 W6 Day 27 walkthrough that an operator under stress could fall into : 1. Typo'd STAGING_URL pointing at production. The script accepted any URL with no sanity check, so `STAGING_URL=https://veza.fr ...` would happily POST /orders and charge a real card on the first run. Fix: heuristic detection (URL doesn't contain "staging", "localhost" or "127.0.0.1" → treat as prod) refuses to run unless CONFIRM_PRODUCTION=1 is explicitly set. 2. No way to rehearse the flow without spending money. Added DRY_RUN=1 that exits cleanly after step 2 (product listing) — exercises auth, API plumbing, and the staging product fixture without creating an order. 3. No final confirm before the actual charge. On a prod target, after the product is picked and before the POST /orders fires, the script now prints the {product_id, price, operator, endpoint} block and demands the operator type the literal word `CHARGE`. Any other answer aborts with exit code 2. Together these turn "STAGING_URL typo = burnt 5 EUR" into "STAGING_URL typo = exit code 3 with explanation". The wrapper docs in docs/PAYMENT_E2E_LIVE_REPORT.md already mention card-charge risk in prose; these guards enforce it at exec time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c644cff03
commit
05b1d81d30
1 changed files with 79 additions and 0 deletions
|
|
@ -42,6 +42,17 @@ OPERATOR_EMAIL=${OPERATOR_EMAIL:-?}
|
|||
OPERATOR_PASSWORD=${OPERATOR_PASSWORD:-?}
|
||||
ORDER_POLL_TIMEOUT=${ORDER_POLL_TIMEOUT:-300}
|
||||
ORDER_POLL_INTERVAL=${ORDER_POLL_INTERVAL:-5}
|
||||
# v1.0.10 polish safety guards:
|
||||
# DRY_RUN=1 — skip the POST /orders + payment steps; rehearse
|
||||
# the login + product-listing + license-poll path
|
||||
# end-to-end on staging without spending a euro.
|
||||
# CONFIRM_PRODUCTION=1 — required when STAGING_URL points at the live
|
||||
# environment. Without it the script refuses to
|
||||
# run, so a typo (`STAGING_URL=https://veza.fr`
|
||||
# on a sandbox-targeted command) can't accidentally
|
||||
# charge a real card.
|
||||
DRY_RUN=${DRY_RUN:-0}
|
||||
CONFIRM_PRODUCTION=${CONFIRM_PRODUCTION:-0}
|
||||
|
||||
SESSION_DATE="$(date +%Y%m%d-%H%M)"
|
||||
SESSION_LOG="${REPO_ROOT}/docs/PAYMENT_E2E_LIVE_REPORT.md.session-${SESSION_DATE}.log"
|
||||
|
|
@ -64,6 +75,43 @@ require jq
|
|||
[ "$OPERATOR_EMAIL" = "?" ] && fail "OPERATOR_EMAIL env var required" 3
|
||||
[ "$OPERATOR_PASSWORD" = "?" ] && fail "OPERATOR_PASSWORD env var required" 3
|
||||
|
||||
# Heuristic: any URL that doesn't include the substring "staging" is
|
||||
# treated as production. Operators on a non-veza-domain (custom env)
|
||||
# can still run the script; they just have to pass CONFIRM_PRODUCTION=1.
|
||||
TARGET_LOOKS_LIKE_PROD=0
|
||||
if [[ ! "$STAGING_URL" =~ staging ]] && [[ ! "$STAGING_URL" =~ localhost ]] && [[ ! "$STAGING_URL" =~ 127\.0\.0\.1 ]]; then
|
||||
TARGET_LOOKS_LIKE_PROD=1
|
||||
fi
|
||||
|
||||
if [ "$TARGET_LOOKS_LIKE_PROD" = "1" ] && [ "$CONFIRM_PRODUCTION" != "1" ]; then
|
||||
cat >&2 <<EOF
|
||||
|
||||
================================================================
|
||||
ABORTING — production target detected without explicit confirmation
|
||||
================================================================
|
||||
|
||||
STAGING_URL=$STAGING_URL does not contain "staging", "localhost" or
|
||||
"127.0.0.1", so this script will refuse to run by default to prevent
|
||||
an accidental real-card charge.
|
||||
|
||||
If you genuinely want to run against production, re-invoke with:
|
||||
|
||||
CONFIRM_PRODUCTION=1 \\
|
||||
STAGING_URL=$STAGING_URL \\
|
||||
OPERATOR_EMAIL=$OPERATOR_EMAIL \\
|
||||
OPERATOR_PASSWORD=... \\
|
||||
bash scripts/payment-e2e-walkthrough.sh
|
||||
|
||||
Or set DRY_RUN=1 to rehearse the flow without making the actual charge.
|
||||
================================================================
|
||||
EOF
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
log "DRY_RUN=1 — order creation + payment + refund steps will be SKIPPED"
|
||||
fi
|
||||
|
||||
# api wrapper that tee's request + response to the session log so the
|
||||
# operator can copy-paste the full trace into the report.
|
||||
api() {
|
||||
|
|
@ -134,8 +182,39 @@ log " ✓ price : $PRODUCT_PRICE"
|
|||
# --------------------------------------------------------------------
|
||||
# Step 3 : POST /orders.
|
||||
# --------------------------------------------------------------------
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
log ""
|
||||
log "step 3 : POST /api/v1/marketplace/orders — SKIPPED (dry-run)"
|
||||
log "================================================================"
|
||||
log "DRY-RUN PASS : login + product list + license-mine endpoints reached"
|
||||
log "Run without DRY_RUN to exercise the real charge + refund flow."
|
||||
log "================================================================"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log ""
|
||||
log "step 3 : POST /api/v1/marketplace/orders"
|
||||
|
||||
# v1.0.10 polish: confirm prompt before the actual charge so a typo'd
|
||||
# product_id or wrong operator account can't quietly burn 5 EUR.
|
||||
if [ "$TARGET_LOOKS_LIKE_PROD" = "1" ]; then
|
||||
log ""
|
||||
log "================================================================"
|
||||
log "FINAL CONFIRMATION — about to charge a real card on production"
|
||||
log "================================================================"
|
||||
log " product_id : $PRODUCT_ID"
|
||||
log " price : $PRODUCT_PRICE"
|
||||
log " operator : $OPERATOR_EMAIL"
|
||||
log " endpoint : ${STAGING_URL}/api/v1/marketplace/orders"
|
||||
log ""
|
||||
prompt "Type the literal word 'CHARGE' to proceed (anything else aborts) :"
|
||||
read -r confirm_word
|
||||
if [ "$confirm_word" != "CHARGE" ]; then
|
||||
fail "operator did not confirm the charge ($confirm_word) — aborting" 2
|
||||
fi
|
||||
log " operator confirmed CHARGE — proceeding"
|
||||
fi
|
||||
|
||||
order_body="{\"items\":[{\"product_id\":\"${PRODUCT_ID}\"}]}"
|
||||
order_resp=$(api POST /api/v1/marketplace/orders "$order_body" 2>/dev/null)
|
||||
ORDER_ID=$(echo "$order_resp" | jq -r '.data.order.id // .data.id // .id // ""')
|
||||
|
|
|
|||
Loading…
Reference in a new issue