merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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