test_merchant_tokenfamilies.sh (12413B)
1 #!/bin/bash 2 # This file is part of TALER 3 # Copyright (C) 2026 Taler Systems SA 4 # 5 # TALER is free software; you can redistribute it and/or modify 6 # it under the terms of the GNU General Public License as 7 # published by the Free Software Foundation; either version 3, or 8 # (at your option) any later version. 9 # 10 # TALER is distributed in the hope that it will be useful, but 11 # WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public 16 # License along with TALER; see the file COPYING. If not, see 17 # <http://www.gnu.org/licenses/> 18 # 19 20 # Cleanup to run whenever we exit 21 function my_cleanup() 22 { 23 for n in $(jobs -p) 24 do 25 kill "$n" 2> /dev/null || true 26 done 27 wait 28 if [ -n "${LAST_RESPONSE+x}" ] 29 then 30 rm -f "${LAST_RESPONSE}" 31 fi 32 } 33 34 35 # Replace with 0 for nexus... 36 USE_FAKEBANK=1 37 if [ 1 = "$USE_FAKEBANK" ] 38 then 39 ACCOUNT="exchange-account-2" 40 BANK_FLAGS="-f -d x-taler-bank -u $ACCOUNT" 41 BANK_URL="http://localhost:8082/" 42 else 43 ACCOUNT="exchange-account-1" 44 BANK_FLAGS="-ns -d iban -u $ACCOUNT" 45 BANK_URL="http://localhost:18082/" 46 echo -n "Testing for libeufin-bank" 47 libeufin-bank --help >/dev/null </dev/null || exit_skip " MISSING" 48 echo " FOUND" 49 fi 50 51 . setup.sh 52 53 setup -c test_template.conf -m 54 CONF="test_template.conf.edited" 55 LAST_RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" test_response.conf-XXXXXX) 56 WALLET_DB=$(mktemp -p "${TMPDIR:-/tmp}" test_wallet.json-XXXXXX) 57 EXCHANGE_URL="http://localhost:8081"/ 58 59 echo -n "Configuring 'admin' instance ..." >&2 60 61 STATUS=$(curl -H "Content-Type: application/json" -X POST \ 62 http://localhost:9966/management/instances \ 63 -d '{"auth":{"method":"token","password":"new_pw"},"id":"admin","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ 64 -w "%{http_code}" \ 65 -s \ 66 -o /dev/null) 67 68 if [ "$STATUS" != "204" ] 69 then 70 exit_fail "Expected 204, instance created. got: $STATUS" >&2 71 fi 72 73 BASIC_AUTH=$(echo -n admin:new_pw | base64) 74 75 STATUS=$(curl -H "Content-Type: application/json" -X POST \ 76 -H "Authorization: Basic $BASIC_AUTH" \ 77 http://localhost:9966/private/token \ 78 -d '{"scope":"spa"}' \ 79 -w "%{http_code}" -s -o $LAST_RESPONSE) 80 81 82 if [ "$STATUS" != "200" ] 83 then 84 exit_fail "Expected 200 OK. Got: $STATUS" 85 fi 86 87 BEARER_TOKEN=$(jq -e -r .access_token < "$LAST_RESPONSE") 88 89 echo " OK" >&2 90 91 echo -n "Setting up bank account..." >&2 92 93 STATUS=$(curl -H "Content-Type: application/json" \ 94 -X POST \ 95 -H "Authorization: Bearer $BEARER_TOKEN" \ 96 http://localhost:9966/private/accounts \ 97 -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/43?receiver-name=user43"}' \ 98 -w "%{http_code}" \ 99 -s \ 100 -o "$LAST_RESPONSE") 101 102 if [ "$STATUS" != "200" ] 103 then 104 cat "$LAST_RESPONSE" >&2 105 exit_fail "Expected 200 OK. Got: $STATUS" 106 fi 107 108 109 # CREATE A DISCOUNT TOKEN FAMILY 110 # 111 echo -n "Creating discount token family..." 112 VALID_AFTER="{\"t_s\": $(date +%s)}" # now 113 VALID_BEFORE="{\"t_s\": $(date +%s -d "+300 days")}" # 300 days from now 114 DURATION="{\"d_us\": $(expr 3 \* 60 \* 1000000)}" # 3 minutes 115 GRANULARITY="{\"d_us\": $(expr 60 \* 1000000)}" # 1 minute 116 STATUS=$(curl 'http://localhost:9966/private/tokenfamilies' \ 117 -X POST \ 118 -H "Authorization: Bearer $BEARER_TOKEN" \ 119 -d "{\"kind\": \"discount\", \"slug\":\"test-discount\", \"name\": \"Test discount\", \"description\": \"Less money $$\", \"description_i18n\": {\"en\": \"Less money $$\", \"es\": \"Menos dinero $$\"}, \"valid_after\": $VALID_AFTER, \"valid_before\": $VALID_BEFORE, \"duration\": $DURATION, \"validity_granularity\": $GRANULARITY}" \ 120 -w "%{http_code}" \ 121 -s \ 122 -o "$LAST_RESPONSE") 123 if [ "$STATUS" != "204" ] 124 then 125 cat "$LAST_RESPONSE" >&2 126 exit_fail "Expected '204 OK' response. Got instead $STATUS" 127 fi 128 echo "Ok" 129 130 # 131 # CREATE A SUBSCRIPTION TOKEN FAMILY 132 # 133 echo -n "Creating subscription token family..." 134 VALID_AFTER="{\"t_s\": $(date +%s)}" # now 135 VALID_BEFORE="{\"t_s\": $(date +%s -d "+30 days")}" # 300 days from now 136 DURATION="{\"d_us\": $(expr 3 \* 60 \* 1000000)}" # 3 minutes 137 GRANULARITY="{\"d_us\": $(expr 60 \* 1000000)}" # 1 minute 138 STATUS=$(curl 'http://localhost:9966/private/tokenfamilies' \ 139 -X POST \ 140 -H "Authorization: Bearer $BEARER_TOKEN" \ 141 -d "{\"kind\": \"subscription\", \"slug\":\"test-subscription\", \"name\": \"Test subscription\", \"description\": \"Money per month\", \"description_i18n\": {\"en\": \"Money $$$ per month\", \"es\": \"Dinero $$$ al mes\"}, \"valid_after\": $VALID_AFTER, \"valid_before\": $VALID_BEFORE, \"duration\": $DURATION, \"validity_granularity\": $GRANULARITY}" \ 142 -w "%{http_code}" \ 143 -s \ 144 -o "$LAST_RESPONSE") 145 if [ "$STATUS" != "204" ] 146 then 147 cat "$LAST_RESPONSE" >&2 148 exit_fail "Expected '204 OK' response. Got instead $STATUS" 149 fi 150 echo "Ok" 151 152 echo "Time traveling merchant 10 minutes into the future ..." 153 154 # Kill merchant 155 kill -TERM "$SETUP_PID" 156 wait 157 unset SETUP_PID 158 159 setup -c test_template.conf \ 160 -ef \ 161 -u "exchange-account-2" \ 162 -r "merchant-exchange-default" 163 164 taler-merchant-exchangekeyupdate \ 165 -c "${CONF}" \ 166 -L DEBUG \ 167 -t \ 168 2> taler-merchant-exchangekeyupdate2.log 169 # 600000000 = 10 minutes 170 taler-merchant-httpd \ 171 -c "${CONF}" \ 172 -L DEBUG \ 173 --timetravel=600000000 \ 174 2> taler-merchant-httpd2.log & 175 # Install cleanup handler (except for kill -9) 176 trap my_cleanup EXIT 177 178 echo -n "Waiting for the merchant..." >&2 179 # Wait for merchant to be available (usually the slowest) 180 for n in $(seq 1 50) 181 do 182 echo -n "." >&2 183 sleep 0.1 184 OK=0 185 # merchant 186 wget --waitretry=0 \ 187 --timeout=1 \ 188 http://localhost:9966/ \ 189 -o /dev/null \ 190 -O /dev/null \ 191 >/dev/null || continue 192 OK=1 193 break 194 done 195 196 if [ "x$OK" != "x1" ] 197 then 198 exit_fail "Failed to (re)start merchant backend" 199 fi 200 201 echo " OK" >&2 202 203 echo -n "Preparing wallet with coins ..." 204 rm -f "$WALLET_DB" 205 taler-wallet-cli \ 206 --no-throttle \ 207 --wallet-db="$WALLET_DB" \ 208 api \ 209 --expect-success 'withdrawTestBalance' \ 210 "$(jq -n ' 211 { 212 amount: "TESTKUDOS:99", 213 corebankApiBaseUrl: $BANK_URL, 214 exchangeBaseUrl: $EXCHANGE_URL 215 }' \ 216 --arg BANK_URL "${BANK_URL}" \ 217 --arg EXCHANGE_URL "$EXCHANGE_URL" 218 )" 2>wallet-withdraw-1.err >wallet-withdraw-1.out 219 echo -n "." 220 # FIXME-MS: add logic to have nexus check immediately here. 221 # sleep 10 222 echo -n "." 223 # NOTE: once libeufin can do long-polling, we should 224 # be able to reduce the delay here and run wirewatch 225 # always in the background via setup 226 taler-exchange-wirewatch \ 227 -a "$ACCOUNT" \ 228 -L "INFO" \ 229 -c "$CONF" \ 230 -t &> taler-exchange-wirewatch.out 231 echo -n "." 232 timeout 60 taler-wallet-cli \ 233 --wallet-db="$WALLET_DB" \ 234 run-until-done \ 235 2>wallet-withdraw-finish-1.err \ 236 >wallet-withdraw-finish-1.out 237 echo " OK" 238 239 CURRENCY_COUNT=$(taler-wallet-cli --wallet-db="$WALLET_DB" balance | jq '.balances|length') 240 if [ "$CURRENCY_COUNT" = "0" ] 241 then 242 exit_fail "Expected least one currency, withdrawal failed. check log." 243 fi 244 245 246 247 RANDOM_IMG='data:image/png;base64,abcdefg' 248 249 echo -n "Creating subscribeable order..." 250 STATUS=$(curl 'http://localhost:9966/private/orders' \ 251 -X POST \ 252 -H "Authorization: Bearer $BEARER_TOKEN" \ 253 -d '{"create_token":true,"refund_delay":{"d_us":0},"order":{"version":1,"summary":"Expensive purchase","products":[{"description":"Expensive subscription","quantity":1,"unit":"pieces","price":"TESTKUDOS:10"}],"choices":[{"amount":"TESTKUDOS:10","inputs":[],"outputs":[{"type":"token","token_family_slug":"test-subscription","count":1}]},{"amount":"TESTKUDOS:0","inputs":[{"type":"token","token_family_slug":"test-subscription","count":1}],"outputs":[{"type":"token","token_family_slug":"test-subscription","count":1}]}]}}' \ 254 -w "%{http_code}" \ 255 -s \ 256 -o "$LAST_RESPONSE") 257 258 if [ "$STATUS" != "200" ] 259 then 260 cat "$LAST_RESPONSE" >&2 261 exit_fail "Expected 200, order created. got: $STATUS" 262 fi 263 echo "OK" 264 265 echo -n "Fetching payment URL " 266 267 ORDER_ID=$(jq -r .order_id < "$LAST_RESPONSE") 268 TOKEN=$(jq -r .token < "$LAST_RESPONSE") 269 270 STATUS=$(curl "http://localhost:9966/private/orders/${ORDER_ID}" \ 271 -H "Authorization: Bearer $BEARER_TOKEN" \ 272 -w "%{http_code}" \ 273 -s \ 274 -o "$LAST_RESPONSE") 275 276 if [ "$STATUS" != "200" ] 277 then 278 cat "$LAST_RESPONSE" >&2 279 exit_fail "Expected 200, getting order info. got: $STATUS" 280 fi 281 282 PAY_URL=$(jq -e -r .taler_pay_uri < "$LAST_RESPONSE") 283 284 echo "OK" 285 286 287 NOW=$(date +%s) 288 289 echo -n "Pay for subscription (choice 1) at ${PAY_URL} ..." 290 echo "1" \ 291 | taler-wallet-cli \ 292 --no-throttle \ 293 --timetravel=600000000 \ 294 --wallet-db="$WALLET_DB" \ 295 handle-uri "${PAY_URL}" \ 296 -y 2> wallet-pay1.err > wallet-pay1.log 297 timeout 60 taler-wallet-cli \ 298 --no-throttle \ 299 --timetravel=600000000 \ 300 --wallet-db="$WALLET_DB" \ 301 run-until-done \ 302 2> wallet-finish-pay1.err \ 303 > wallet-finish-pay1.log 304 NOW2=$(date +%s) 305 echo " OK (took $(( NOW2 - NOW )) secs )" 306 307 echo -n "Checking order was paid ..." 308 309 STATUS=$(curl "http://localhost:9966/private/orders/${ORDER_ID}" \ 310 -H "Authorization: Bearer $BEARER_TOKEN" \ 311 -w "%{http_code}" \ 312 -s \ 313 -o "$LAST_RESPONSE") 314 315 if [ "$STATUS" != "200" ] 316 then 317 cat "$LAST_RESPONSE" >&2 318 exit_fail "Expected 200, after pay. got: $STATUS" 319 fi 320 321 ORDER_STATUS=$(jq -r .order_status < "$LAST_RESPONSE") 322 323 if [ "$ORDER_STATUS" != "paid" ] 324 then 325 cat "$LAST_RESPONSE" >&2 326 exit_fail "Order status should be 'paid'. got: $ORDER_STATUS" 327 fi 328 echo " OK" 329 330 # FIXME: test also paying with the subscription token... 331 332 333 334 echo -n "Creating discountable order..." 335 STATUS=$(curl 'http://localhost:9966/private/orders' \ 336 -X POST \ 337 -H "Authorization: Bearer $BEARER_TOKEN" \ 338 -d '{"create_token":true,"refund_delay":{"d_us":0},"order":{"version":1,"summary":"Expensive subscription","products":[{"description":"Simple purchase","quantity":1,"unit":"pieces","price":"TESTKUDOS:10"}],"choices":[{"amount":"TESTKUDOS:10","inputs":[],"outputs":[{"type":"token","token_family_slug":"test-discount","count":1}]},{"amount":"TESTKUDOS:9","inputs":[{"type":"token","token_family_slug":"test-discount","count":1}],"outputs":[]}]}}' \ 339 -w "%{http_code}" \ 340 -s \ 341 -o "$LAST_RESPONSE") 342 343 if [ "$STATUS" != "200" ] 344 then 345 cat "$LAST_RESPONSE" >&2 346 exit_fail "Expected 200, order created. got: $STATUS" 347 fi 348 echo "OK" 349 350 ORDER_ID=$(jq -r .order_id < "$LAST_RESPONSE") 351 TOKEN=$(jq -r .token < "$LAST_RESPONSE") 352 353 echo -n "Fetching payment URL " 354 355 ORDER_ID=$(jq -r .order_id < "$LAST_RESPONSE") 356 TOKEN=$(jq -r .token < "$LAST_RESPONSE") 357 358 STATUS=$(curl "http://localhost:9966/private/orders/${ORDER_ID}" \ 359 -X POST \ 360 -H "Authorization: Bearer $BEARER_TOKEN" \ 361 -w "%{http_code}" \ 362 -s \ 363 -o "$LAST_RESPONSE") 364 365 if [ "$STATUS" != "200" ] 366 then 367 cat "$LAST_RESPONSE" >&2 368 exit_fail "Expected 200, getting order info. got: $STATUS" 369 fi 370 371 PAY_URL=$(jq -e -r .taler_pay_uri < "$LAST_RESPONSE") 372 373 echo "OK" 374 375 376 NOW=$(date +%s) 377 378 echo -n "Pay without discount (choice 0) at ${PAY_URL} ..." 379 echo "0" \ 380 | taler-wallet-cli \ 381 --no-throttle \ 382 --timetravel=600000000 \ 383 --wallet-db="$WALLET_DB" \ 384 handle-uri "${PAY_URL}" \ 385 -y 2> wallet-pay1.err > wallet-pay1.log 386 timeout 60 taler-wallet-cli \ 387 --no-throttle \ 388 --timetravel=600000000 \ 389 --wallet-db="$WALLET_DB" \ 390 run-until-done \ 391 2> wallet-finish-pay1.err \ 392 > wallet-finish-pay1.log 393 NOW2=$(date +%s) 394 echo " OK (took $(( NOW2 - NOW )) secs )" 395 396 echo -n "Checking order was paid ..." 397 398 STATUS=$(curl "http://localhost:9966/private/orders/${ORDER_ID}" \ 399 -X POST \ 400 -H "Authorization: Bearer $BEARER_TOKEN" \ 401 -w "%{http_code}" \ 402 -s \ 403 -o "$LAST_RESPONSE") 404 405 if [ "$STATUS" != "200" ] 406 then 407 cat "$LAST_RESPONSE" >&2 408 exit_fail "Expected 200, after pay. got: $STATUS" 409 fi 410 411 ORDER_STATUS=$(jq -r .order_status < "$LAST_RESPONSE") 412 413 if [ "$ORDER_STATUS" != "paid" ] 414 then 415 cat "$LAST_RESPONSE" >&2 416 exit_fail "Order status should be 'paid'. got: $ORDER_STATUS" 417 fi 418 echo " OK" 419 420 # FIXME: test also paying with the discount token... 421 422 echo "Test PASSED" 423 424 exit 0