This commit is contained in:
parent
7098ac75cb
commit
df91762271
29 changed files with 3079 additions and 838 deletions
57
tools/dev-credit.sh
Executable file
57
tools/dev-credit.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/sh
|
||||
# Pretend the bank reported an incoming transfer, so a dev order can be paid
|
||||
# without a bank.
|
||||
#
|
||||
# The transfer rail settles an order when a credit quoting its reference shows
|
||||
# up in the credits file. In production that file is filled by
|
||||
# `catcrafts-server --pull-credits` reading the real account; here it is filled
|
||||
# by hand. Same file, same format, same matching code — which is the point:
|
||||
# this exercises the real settlement path rather than a test double.
|
||||
#
|
||||
# tools/dev-credit.sh <orders-file> <reference> <amount-in-cents> [method]
|
||||
#
|
||||
# Example, paying order CC-2B6457 the €570.43 it is waiting for:
|
||||
# tools/dev-credit.sh /tmp/dev/orders.jsonl CC-2B6457 57043
|
||||
#
|
||||
# The reference is matched forgivingly (case, spacing and punctuation are
|
||||
# ignored, and the RF… form works too), so it is worth deliberately mangling it
|
||||
# to watch that hold:
|
||||
# tools/dev-credit.sh /tmp/dev/orders.jsonl "betaling cc 2b6457 bedankt" 57043
|
||||
set -eu
|
||||
|
||||
if [ "$#" -lt 3 ]; then
|
||||
echo "usage: $0 <orders-file> <reference> <amount-in-cents> [method]" >&2
|
||||
echo " method defaults to sepa; try 'ideal' or 'card' to see the ledger's" >&2
|
||||
echo " via column change, which is what decides if an order is safe to ship." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ORDERS="$1"
|
||||
REFERENCE="$2"
|
||||
CENTS="$3"
|
||||
METHOD="${4:-sepa}"
|
||||
CREDITS="$ORDERS.transfer-credits.jsonl"
|
||||
|
||||
case "$CENTS" in
|
||||
''|*[!0-9]*) echo "$0: amount must be whole cents, got '$CENTS'" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
# A unique id per line, because the rail deduplicates on it: appending the same
|
||||
# id twice is deliberately a no-op, which is what stops a repeated pull from
|
||||
# double-crediting an order. Using a counter keeps each hand-made credit
|
||||
# distinct without needing a clock.
|
||||
n=1
|
||||
if [ -f "$CREDITS" ]; then
|
||||
n=$(( $(wc -l < "$CREDITS") + 1 ))
|
||||
fi
|
||||
|
||||
# Escape the two characters that would break the line-per-record format. The
|
||||
# reference is free text on purpose so it can be mangled realistically.
|
||||
escaped=$(printf '%s' "$REFERENCE" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
||||
|
||||
printf '{"id":"dev-%s","reference":"%s","amount_minor":%s,"method":"%s"}\n' \
|
||||
"$n" "$escaped" "$CENTS" "$METHOD" >> "$CREDITS"
|
||||
|
||||
echo "credited $CENTS cents quoting '$REFERENCE' (method $METHOD)"
|
||||
echo "wrote $CREDITS"
|
||||
echo "the reconciler sweeps about once a minute; watch the dev log for 'paid'"
|
||||
62
tools/dev.sh
62
tools/dev.sh
|
|
@ -133,30 +133,41 @@ cat > "$WORK/Caddyfile" <<EOF
|
|||
EOF
|
||||
|
||||
# Rail selection for dev:
|
||||
# * a repo-root .env (gitignored, never committed) is sourced if present —
|
||||
# put MOLLIE_API_KEY=test_… there to point dev at Mollie's real test mode;
|
||||
# * DEV_RAIL=fake|mollie overrides the automatic choice;
|
||||
# * default with no key is the fake rail: full order lifecycle, no network.
|
||||
# "Pay" an order with: touch $WORK/orders.jsonl.fake-paid
|
||||
# * a repo-root .env (gitignored, never committed) is sourced if present;
|
||||
# * DEV_RAIL=transfer|fake overrides the automatic choice;
|
||||
# * default is TRANSFER, because that is what production serves. Dev used to
|
||||
# default to the fake rail, which meant the bank rail's actual page — the
|
||||
# IBAN, the beneficiary name, the reference, the whole thing a buyer reads
|
||||
# — could not be looked at locally at all.
|
||||
#
|
||||
# A live_ key is refused outright. Dev creates throwaway orders; pointing them
|
||||
# at real money collection is never what anyone meant.
|
||||
# The transfer rail needs no credential, so dev can run the REAL rail rather
|
||||
# than a stand-in: it only needs an account to name. If .env does not name one,
|
||||
# obviously-fake values are used, and they are labelled as such on purpose —
|
||||
# never make fixture data look real (a sample financials file was once mistaken
|
||||
# for the actual books).
|
||||
#
|
||||
# Nothing here can collect real money: the transfer rail only RENDERS account
|
||||
# details, and settlement comes from a credits file this machine writes.
|
||||
if [ -f .env ]; then
|
||||
set -a; . ./.env; set +a
|
||||
fi
|
||||
RAIL="${DEV_RAIL:-}"
|
||||
if [ -z "$RAIL" ]; then
|
||||
RAIL=fake
|
||||
[ -n "${MOLLIE_API_KEY:-}" ] && RAIL=mollie
|
||||
fi
|
||||
if [ "$RAIL" = mollie ]; then
|
||||
case "${MOLLIE_API_KEY:-}" in
|
||||
test_*) echo "dev: payments via Mollie TEST mode" ;;
|
||||
live_*) echo "dev: refusing to run dev against a LIVE Mollie key." >&2
|
||||
echo "dev: live keys belong in /etc/catcrafts/payments.env on the server." >&2
|
||||
exit 1 ;;
|
||||
*) echo "dev: MOLLIE_API_KEY is not a test_ or live_ key" >&2; exit 1 ;;
|
||||
esac
|
||||
RAIL="${DEV_RAIL:-transfer}"
|
||||
if [ "$RAIL" = transfer ]; then
|
||||
# Refuse a real-looking IBAN that is not yours to be paid into by mistake?
|
||||
# No — the opposite risk matters here. These values are only ever RENDERED
|
||||
# in dev; nothing can be paid. What must not happen is dev quietly showing
|
||||
# the PRODUCTION account while someone screenshots the page, so when .env
|
||||
# supplies nothing the placeholders say plainly that they are placeholders.
|
||||
: "${TRANSFER_IBAN:=NL00DEVB0000000000}"
|
||||
: "${TRANSFER_BENEFICIARY:=DEV PLACEHOLDER, not a real account}"
|
||||
: "${TRANSFER_BIC:=DEVBNL2A}"
|
||||
export TRANSFER_IBAN TRANSFER_BENEFICIARY TRANSFER_BIC
|
||||
if [ "$TRANSFER_IBAN" = NL00DEVB0000000000 ]; then
|
||||
echo "dev: bank transfer rail with PLACEHOLDER account details"
|
||||
echo "dev: put TRANSFER_IBAN / TRANSFER_BENEFICIARY / TRANSFER_BIC in .env to preview the real ones"
|
||||
else
|
||||
echo "dev: bank transfer rail, account $TRANSFER_IBAN ($TRANSFER_BENEFICIARY)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# /financials has two inputs and a fresh dev run has neither: sales fold out of
|
||||
|
|
@ -247,7 +258,16 @@ cat <<EOF
|
|||
Edit the aggregates and just refresh — they are re-read every request:
|
||||
\$EDITOR $WORK/orders.jsonl.financials.json")
|
||||
Payment rail: $RAIL$([ "$RAIL" = fake ] && printf '%s' " — simulate a customer paying with:
|
||||
touch $WORK/orders.jsonl.fake-paid")
|
||||
touch $WORK/orders.jsonl.fake-paid")$([ "$RAIL" = transfer ] && printf '%s' " — the order page shows
|
||||
the account details a buyer would transfer to. To simulate the money arriving,
|
||||
append the credit the bank would have reported, using the order's own
|
||||
reference (the CC-… code on the order page):
|
||||
|
||||
tools/dev-credit.sh $WORK/orders.jsonl CC-XXXXXX 57043
|
||||
|
||||
The reconciler picks it up within a minute and the order flips to paid.
|
||||
Reference matching is deliberately forgiving, so try lower case, spaces, or
|
||||
the RF… form to see that they all still settle the same order.")
|
||||
Ctrl-C to stop.
|
||||
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ ORDERS=$(systemctl cat "$UNIT" | sed -n 's/^[[:space:]]*--orders=\([^ \\]*\).*/\
|
|||
[ -n "$ORDERS" ] || ORDERS=/var/lib/catcrafts/orders.jsonl
|
||||
ENVF=$(systemctl cat "$UNIT" | sed -n 's/^EnvironmentFile=-\{0,1\}\(.*\)/\1/p' | head -1)
|
||||
[ -n "$ENVF" ] || ENVF=/etc/catcrafts/payments.env
|
||||
[ -e "$ENVF" ] || { echo "apply: $ENVF does not exist — is the Mollie side even configured?" >&2; exit 1; }
|
||||
[ -e "$ENVF" ] || { echo "apply: $ENVF does not exist — is the bank rail even configured?" >&2; exit 1; }
|
||||
|
||||
# Respect an explicit EURC_POOL override if one is already configured;
|
||||
# otherwise the server's default: the pool hangs off the orders path.
|
||||
|
|
@ -198,8 +198,8 @@ install -m 0644 "$work/chains.json" "$CHAINS_DEST"
|
|||
#
|
||||
# Two bugs lived in the one-liner this replaces. First, payments.env is
|
||||
# hand-maintained, so its last line may have no trailing newline — and then a
|
||||
# bare >> concatenated onto it, turning MOLLIE_API_KEY=live_abc into
|
||||
# MOLLIE_API_KEY=live_abcEURC_CHAINS=/etc/... : both rails broken, and the
|
||||
# bare >> concatenated onto it, turning TRANSFER_IBAN=NL00 into
|
||||
# TRANSFER_IBAN=NL00EURC_CHAINS=/etc/... : both rails broken, and the
|
||||
# rollback below could not even see it because the line no longer started with
|
||||
# EURC_CHAINS. Second, the rollback deleted EVERY EURC_CHAINS= line, including
|
||||
# one the operator had set themselves pointing at a different chains file — so
|
||||
|
|
|
|||
Loading…
Reference in a new issue