This commit is contained in:
parent
aaa7a8ce99
commit
413740af2e
4 changed files with 134 additions and 13 deletions
78
tools/register-bunq-webhook.sh
Executable file
78
tools/register-bunq-webhook.sh
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
#!/bin/sh
|
||||
# Tell bunq where to push payment notifications.
|
||||
#
|
||||
# Run from a machine whose address the bunq API key permits — the web host is
|
||||
# not one, which is the whole reason webhooks exist in this setup. The session
|
||||
# it uses comes from the context file that tools/pull-and-ship-credits.sh
|
||||
# created, so run that at least once first.
|
||||
#
|
||||
# tools/register-bunq-webhook.sh 'https://catcrafts.net/hooks/bunq/<secret>'
|
||||
# tools/register-bunq-webhook.sh --show # list what is registered
|
||||
#
|
||||
# THIS REPLACES THE WHOLE FILTER SET. bunq treats the POST as "here is the
|
||||
# complete list", so a call that omits an existing filter deletes it. Every
|
||||
# filter wanted has to be in the one request, which is why --show exists: look
|
||||
# before overwriting.
|
||||
#
|
||||
# The target URL is never echoed: it is a shared secret, and this script's
|
||||
# output tends to end up pasted into issues and chat logs.
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
CTX="${CREDITS_LOCAL:-$HOME/.cache/catcrafts}/orders.jsonl.transfer-credits.jsonl.bunq-context.json"
|
||||
[ -f "$CTX" ] || { echo "$0: no bunq context at $CTX — run tools/pull-and-ship-credits.sh first" >&2; exit 1; }
|
||||
|
||||
SESSION=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["session_token"])' "$CTX")
|
||||
USERID=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["user_id"])' "$CTX")
|
||||
[ -n "$SESSION" ] && [ "$USERID" != "0" ] || { echo "$0: context has no session — run the pull once" >&2; exit 1; }
|
||||
|
||||
API="https://api.bunq.com/v1/user/$USERID/notification-filter-url"
|
||||
HDRS="-H Content-Type:application/json -H X-Bunq-Client-Authentication:$SESSION -H User-Agent:catcrafts.net-register/1.0"
|
||||
|
||||
if [ "${1:-}" = "--show" ]; then
|
||||
# Redact the secret out of the response before it reaches a terminal.
|
||||
# shellcheck disable=SC2086
|
||||
curl -s $HDRS "$API" | python3 -c '
|
||||
import json,sys,re
|
||||
try: d=json.load(sys.stdin)
|
||||
except Exception: print("could not parse bunq response"); sys.exit(1)
|
||||
for item in d.get("Response",[]):
|
||||
f=item.get("NotificationFilterUrl") or {}
|
||||
t=f.get("notification_target","")
|
||||
print(" category=%-22s target=%s" % (f.get("category","?"), re.sub(r"(/hooks/bunq/).*", r"\1<redacted>", t)))
|
||||
'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TARGET="${1:-}"
|
||||
case "$TARGET" in
|
||||
https://*) ;;
|
||||
*) echo "$0: pass the full https:// callback URL (bunq refuses plain http)" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
# MUTATION alone: it fires on every balance change and carries the Payment
|
||||
# object, which is exactly what the handler decodes. Adding PAYMENT as well
|
||||
# would deliver most events twice, and while the handler deduplicates on the
|
||||
# payment id, doubling the traffic to buy nothing is not a trade.
|
||||
BODY=$(python3 -c '
|
||||
import json,sys
|
||||
# No notification_delivery_method: bunq rejects it as "Superfluous" on this
|
||||
# endpoint, because /notification-filter-url already means URL delivery.
|
||||
print(json.dumps({"notification_filters":[
|
||||
{"notification_target":sys.argv[1],
|
||||
"category":"MUTATION"}]}))
|
||||
' "$TARGET")
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
OUT=$(printf '%s' "$BODY" | curl -s -w '\n%{http_code}' $HDRS -X POST --data-binary @- "$API")
|
||||
CODE=$(printf '%s' "$OUT" | tail -1)
|
||||
if [ "$CODE" = 200 ]; then
|
||||
echo "$0: registered MUTATION callbacks (target not shown)"
|
||||
echo "$0: verify with $0 --show"
|
||||
else
|
||||
# The body can echo the target back, so redact before printing an error.
|
||||
printf '%s\n' "$OUT" | sed 's#\(/hooks/bunq/\)[^"]*#\1<redacted>#g' >&2
|
||||
echo "$0: bunq refused the registration (HTTP $CODE)" >&2
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Reference in a new issue